Bug Summary

File:out/../deps/v8/src/wasm/wasm-module.h
Warning:line 443, column 35
Forming reference to null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name module-decoder.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/home/maurizio/node-v18.6.0/out -resource-dir /usr/local/lib/clang/16.0.0 -D _GLIBCXX_USE_CXX11_ABI=1 -D NODE_OPENSSL_CONF_NAME=nodejs_conf -D NODE_OPENSSL_HAS_QUIC -D V8_GYP_BUILD -D V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -D __STDC_FORMAT_MACROS -D OPENSSL_NO_PINSHARED -D OPENSSL_THREADS -D V8_TARGET_ARCH_X64 -D V8_HAVE_TARGET_OS -D V8_TARGET_OS_LINUX -D V8_EMBEDDER_STRING="-node.8" -D ENABLE_DISASSEMBLER -D V8_PROMISE_INTERNAL_FIELD_COUNT=1 -D V8_SHORT_BUILTIN_CALLS -D OBJECT_PRINT -D V8_INTL_SUPPORT -D V8_ATOMIC_OBJECT_FIELD_WRITES -D V8_ENABLE_LAZY_SOURCE_POSITIONS -D V8_USE_SIPHASH -D V8_SHARED_RO_HEAP -D V8_WIN64_UNWINDING_INFO -D V8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH -D V8_SNAPSHOT_COMPRESSION -D V8_ENABLE_WEBASSEMBLY -D V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS -D V8_ALLOCATION_FOLDING -D V8_ALLOCATION_SITE_TRACKING -D V8_SCRIPTORMODULE_LEGACY_LIFETIME -D V8_ADVANCED_BIGINT_ALGORITHMS -D ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC -D UCONFIG_NO_SERVICE=1 -D U_ENABLE_DYLOAD=0 -D U_STATIC_IMPLEMENTATION=1 -D U_HAVE_STD_STRING=1 -D UCONFIG_NO_BREAK_ITERATION=0 -I ../deps/v8 -I ../deps/v8/include -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/inspector-generated-output-root -I ../deps/v8/third_party/inspector_protocol -I /home/maurizio/node-v18.6.0/out/Release/obj/gen -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/generate-bytecode-output-root -I ../deps/icu-small/source/i18n -I ../deps/icu-small/source/common -I ../deps/v8/third_party/zlib -I ../deps/v8/third_party/zlib/google -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward -internal-isystem /usr/local/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-unused-parameter -Wno-return-type -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/home/maurizio/node-v18.6.0/out -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-08-22-142216-507842-1 -x c++ ../deps/v8/src/wasm/module-decoder.cc

../deps/v8/src/wasm/module-decoder.cc

1// Copyright 2015 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#include "src/wasm/module-decoder.h"
6
7#include "src/base/functional.h"
8#include "src/base/platform/platform.h"
9#include "src/base/platform/wrappers.h"
10#include "src/flags/flags.h"
11#include "src/init/v8.h"
12#include "src/logging/counters.h"
13#include "src/logging/metrics.h"
14#include "src/objects/objects-inl.h"
15#include "src/utils/ostreams.h"
16#include "src/wasm/canonical-types.h"
17#include "src/wasm/decoder.h"
18#include "src/wasm/function-body-decoder-impl.h"
19#include "src/wasm/init-expr-interface.h"
20#include "src/wasm/struct-types.h"
21#include "src/wasm/wasm-constants.h"
22#include "src/wasm/wasm-engine.h"
23#include "src/wasm/wasm-limits.h"
24#include "src/wasm/wasm-opcodes-inl.h"
25
26namespace v8 {
27namespace internal {
28namespace wasm {
29
30#define TRACE(...) \
31 do { \
32 if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \
33 } while (false)
34
35namespace {
36
37constexpr char kNameString[] = "name";
38constexpr char kSourceMappingURLString[] = "sourceMappingURL";
39constexpr char kCompilationHintsString[] = "compilationHints";
40constexpr char kBranchHintsString[] = "metadata.code.branch_hint";
41constexpr char kDebugInfoString[] = ".debug_info";
42constexpr char kExternalDebugInfoString[] = "external_debug_info";
43
44const char* ExternalKindName(ImportExportKindCode kind) {
45 switch (kind) {
46 case kExternalFunction:
47 return "function";
48 case kExternalTable:
49 return "table";
50 case kExternalMemory:
51 return "memory";
52 case kExternalGlobal:
53 return "global";
54 case kExternalTag:
55 return "tag";
56 }
57 return "unknown";
58}
59
60} // namespace
61
62const char* SectionName(SectionCode code) {
63 switch (code) {
64 case kUnknownSectionCode:
65 return "Unknown";
66 case kTypeSectionCode:
67 return "Type";
68 case kImportSectionCode:
69 return "Import";
70 case kFunctionSectionCode:
71 return "Function";
72 case kTableSectionCode:
73 return "Table";
74 case kMemorySectionCode:
75 return "Memory";
76 case kGlobalSectionCode:
77 return "Global";
78 case kExportSectionCode:
79 return "Export";
80 case kStartSectionCode:
81 return "Start";
82 case kCodeSectionCode:
83 return "Code";
84 case kElementSectionCode:
85 return "Element";
86 case kDataSectionCode:
87 return "Data";
88 case kTagSectionCode:
89 return "Tag";
90 case kDataCountSectionCode:
91 return "DataCount";
92 case kNameSectionCode:
93 return kNameString;
94 case kSourceMappingURLSectionCode:
95 return kSourceMappingURLString;
96 case kDebugInfoSectionCode:
97 return kDebugInfoString;
98 case kExternalDebugInfoSectionCode:
99 return kExternalDebugInfoString;
100 case kCompilationHintsSectionCode:
101 return kCompilationHintsString;
102 case kBranchHintsSectionCode:
103 return kBranchHintsString;
104 default:
105 return "<unknown>";
106 }
107}
108
109namespace {
110
111bool validate_utf8(Decoder* decoder, WireBytesRef string) {
112 return unibrow::Utf8::ValidateEncoding(
113 decoder->start() + decoder->GetBufferRelativeOffset(string.offset()),
114 string.length());
115}
116
117// Reads a length-prefixed string, checking that it is within bounds. Returns
118// the offset of the string, and the length as an out parameter.
119WireBytesRef consume_string(Decoder* decoder, bool validate_utf8,
120 const char* name) {
121 uint32_t length = decoder->consume_u32v("string length");
122 uint32_t offset = decoder->pc_offset();
123 const byte* string_start = decoder->pc();
124 // Consume bytes before validation to guarantee that the string is not oob.
125 if (length > 0) {
126 decoder->consume_bytes(length, name);
127 if (decoder->ok() && validate_utf8 &&
128 !unibrow::Utf8::ValidateEncoding(string_start, length)) {
129 decoder->errorf(string_start, "%s: no valid UTF-8 string", name);
130 }
131 }
132 return {offset, decoder->failed() ? 0 : length};
133}
134
135namespace {
136SectionCode IdentifyUnknownSectionInternal(Decoder* decoder) {
137 WireBytesRef string = consume_string(decoder, true, "section name");
138 if (decoder->failed()) {
139 return kUnknownSectionCode;
140 }
141 const byte* section_name_start =
142 decoder->start() + decoder->GetBufferRelativeOffset(string.offset());
143
144 TRACE(" +%d section name : \"%.*s\"\n",
145 static_cast<int>(section_name_start - decoder->start()),
146 string.length() < 20 ? string.length() : 20, section_name_start);
147
148 using SpecialSectionPair = std::pair<base::Vector<const char>, SectionCode>;
149 static constexpr SpecialSectionPair kSpecialSections[]{
150 {base::StaticCharVector(kNameString), kNameSectionCode},
151 {base::StaticCharVector(kSourceMappingURLString),
152 kSourceMappingURLSectionCode},
153 {base::StaticCharVector(kCompilationHintsString),
154 kCompilationHintsSectionCode},
155 {base::StaticCharVector(kBranchHintsString), kBranchHintsSectionCode},
156 {base::StaticCharVector(kDebugInfoString), kDebugInfoSectionCode},
157 {base::StaticCharVector(kExternalDebugInfoString),
158 kExternalDebugInfoSectionCode}};
159
160 auto name_vec = base::Vector<const char>::cast(
161 base::VectorOf(section_name_start, string.length()));
162 for (auto& special_section : kSpecialSections) {
163 if (name_vec == special_section.first) return special_section.second;
164 }
165
166 return kUnknownSectionCode;
167}
168} // namespace
169
170// An iterator over the sections in a wasm binary module.
171// Automatically skips all unknown sections.
172class WasmSectionIterator {
173 public:
174 explicit WasmSectionIterator(Decoder* decoder)
175 : decoder_(decoder),
176 section_code_(kUnknownSectionCode),
177 section_start_(decoder->pc()),
178 section_end_(decoder->pc()) {
179 next();
180 }
181
182 bool more() const { return decoder_->ok() && decoder_->more(); }
183
184 SectionCode section_code() const { return section_code_; }
185
186 const byte* section_start() const { return section_start_; }
187
188 uint32_t section_length() const {
189 return static_cast<uint32_t>(section_end_ - section_start_);
190 }
191
192 base::Vector<const uint8_t> payload() const {
193 return {payload_start_, payload_length()};
194 }
195
196 const byte* payload_start() const { return payload_start_; }
197
198 uint32_t payload_length() const {
199 return static_cast<uint32_t>(section_end_ - payload_start_);
200 }
201
202 const byte* section_end() const { return section_end_; }
203
204 // Advances to the next section, checking that decoding the current section
205 // stopped at {section_end_}.
206 void advance(bool move_to_section_end = false) {
207 if (move_to_section_end && decoder_->pc() < section_end_) {
208 decoder_->consume_bytes(
209 static_cast<uint32_t>(section_end_ - decoder_->pc()));
210 }
211 if (decoder_->pc() != section_end_) {
212 const char* msg = decoder_->pc() < section_end_ ? "shorter" : "longer";
213 decoder_->errorf(decoder_->pc(),
214 "section was %s than expected size "
215 "(%u bytes expected, %zu decoded)",
216 msg, section_length(),
217 static_cast<size_t>(decoder_->pc() - section_start_));
218 }
219 next();
220 }
221
222 private:
223 Decoder* decoder_;
224 SectionCode section_code_;
225 const byte* section_start_;
226 const byte* payload_start_;
227 const byte* section_end_;
228
229 // Reads the section code/name at the current position and sets up
230 // the embedder fields.
231 void next() {
232 if (!decoder_->more()) {
233 section_code_ = kUnknownSectionCode;
234 return;
235 }
236 section_start_ = decoder_->pc();
237 uint8_t section_code = decoder_->consume_u8("section code");
238 // Read and check the section size.
239 uint32_t section_length = decoder_->consume_u32v("section length");
240
241 payload_start_ = decoder_->pc();
242 if (decoder_->checkAvailable(section_length)) {
243 // Get the limit of the section within the module.
244 section_end_ = payload_start_ + section_length;
245 } else {
246 // The section would extend beyond the end of the module.
247 section_end_ = payload_start_;
248 }
249
250 if (section_code == kUnknownSectionCode) {
251 // Check for the known "name", "sourceMappingURL", or "compilationHints"
252 // section.
253 // To identify the unknown section we set the end of the decoder bytes to
254 // the end of the custom section, so that we do not read the section name
255 // beyond the end of the section.
256 const byte* module_end = decoder_->end();
257 decoder_->set_end(section_end_);
258 section_code = IdentifyUnknownSectionInternal(decoder_);
259 if (decoder_->ok()) decoder_->set_end(module_end);
260 // As a side effect, the above function will forward the decoder to after
261 // the identifier string.
262 payload_start_ = decoder_->pc();
263 } else if (!IsValidSectionCode(section_code)) {
264 decoder_->errorf(decoder_->pc(), "unknown section code #0x%02x",
265 section_code);
266 section_code = kUnknownSectionCode;
267 }
268 section_code_ = decoder_->failed() ? kUnknownSectionCode
269 : static_cast<SectionCode>(section_code);
270
271 if (section_code_ == kUnknownSectionCode && section_end_ > decoder_->pc()) {
272 // skip to the end of the unknown section.
273 uint32_t remaining = static_cast<uint32_t>(section_end_ - decoder_->pc());
274 decoder_->consume_bytes(remaining, "section payload");
275 }
276 }
277};
278
279} // namespace
280
281// The main logic for decoding the bytes of a module.
282class ModuleDecoderImpl : public Decoder {
283 public:
284 explicit ModuleDecoderImpl(const WasmFeatures& enabled, ModuleOrigin origin)
285 : Decoder(nullptr, nullptr),
286 enabled_features_(enabled),
287 origin_(origin) {}
288
289 ModuleDecoderImpl(const WasmFeatures& enabled, const byte* module_start,
290 const byte* module_end, ModuleOrigin origin)
291 : Decoder(module_start, module_end),
292 enabled_features_(enabled),
293 module_start_(module_start),
294 module_end_(module_end),
295 origin_(origin) {
296 if (end_ < start_) {
297 error(start_, "end is less than start");
298 end_ = start_;
299 }
300 }
301
302 void onFirstError() override {
303 pc_ = end_; // On error, terminate section decoding loop.
304 }
305
306 void DumpModule(const base::Vector<const byte> module_bytes) {
307 std::string path;
308 if (FLAG_dump_wasm_module_path) {
309 path = FLAG_dump_wasm_module_path;
310 if (path.size() &&
311 !base::OS::isDirectorySeparator(path[path.size() - 1])) {
312 path += base::OS::DirectorySeparator();
313 }
314 }
315 // File are named `HASH.{ok,failed}.wasm`.
316 size_t hash = base::hash_range(module_bytes.begin(), module_bytes.end());
317 base::EmbeddedVector<char, 32> buf;
318 SNPrintF(buf, "%016zx.%s.wasm", hash, ok() ? "ok" : "failed");
319 path += buf.begin();
320 size_t rv = 0;
321 if (FILE* file = base::OS::FOpen(path.c_str(), "wb")) {
322 rv = fwrite(module_bytes.begin(), module_bytes.length(), 1, file);
323 base::Fclose(file);
324 }
325 if (rv != 1) {
326 OFStream os(stderrstderr);
327 os << "Error while dumping wasm file to " << path << std::endl;
328 }
329 }
330
331 void StartDecoding(Counters* counters, AccountingAllocator* allocator) {
332 CHECK_NULL(module_)do { if ((__builtin_expect(!!(!((module_) == nullptr)), 0))) {
V8_Fatal("Check failed: %s.", "(module_) == nullptr"); } } while
(false)
;
333 SetCounters(counters);
334 module_.reset(
335 new WasmModule(std::make_unique<Zone>(allocator, "signatures")));
336 module_->initial_pages = 0;
337 module_->maximum_pages = 0;
338 module_->mem_export = false;
339 module_->origin = origin_;
340 }
341
342 void DecodeModuleHeader(base::Vector<const uint8_t> bytes, uint8_t offset) {
343 if (failed()) return;
344 Reset(bytes, offset);
345
346 const byte* pos = pc_;
347 uint32_t magic_word = consume_u32("wasm magic");
348#define BYTES(x) (x & 0xFF), (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF
349 if (magic_word != kWasmMagic) {
350 errorf(pos,
351 "expected magic word %02x %02x %02x %02x, "
352 "found %02x %02x %02x %02x",
353 BYTES(kWasmMagic), BYTES(magic_word));
354 }
355
356 pos = pc_;
357 {
358 uint32_t magic_version = consume_u32("wasm version");
359 if (magic_version != kWasmVersion) {
360 errorf(pos,
361 "expected version %02x %02x %02x %02x, "
362 "found %02x %02x %02x %02x",
363 BYTES(kWasmVersion), BYTES(magic_version));
364 }
365 }
366#undef BYTES
367 }
368
369 bool CheckSectionOrder(SectionCode section_code,
370 SectionCode prev_section_code,
371 SectionCode next_section_code) {
372 if (next_ordered_section_ > next_section_code) {
373 errorf(pc(), "The %s section must appear before the %s section",
374 SectionName(section_code), SectionName(next_section_code));
375 return false;
376 }
377 if (next_ordered_section_ <= prev_section_code) {
378 next_ordered_section_ = prev_section_code + 1;
379 }
380 return true;
381 }
382
383 bool CheckUnorderedSection(SectionCode section_code) {
384 if (has_seen_unordered_section(section_code)) {
385 errorf(pc(), "Multiple %s sections not allowed",
386 SectionName(section_code));
387 return false;
388 }
389 set_seen_unordered_section(section_code);
390 return true;
391 }
392
393 void DecodeSection(SectionCode section_code,
394 base::Vector<const uint8_t> bytes, uint32_t offset,
395 bool verify_functions = true) {
396 if (failed()) return;
2
Taking false branch
397 Reset(bytes, offset);
398 TRACE("Section: %s\n", SectionName(section_code));
3
Taking false branch
4
Loop condition is false. Exiting loop
399 TRACE("Decode Section %p - %p\n", bytes.begin(), bytes.end());
5
Taking false branch
400
401 // Check if the section is out-of-order.
402 if (section_code < next_ordered_section_ &&
6
Loop condition is false. Exiting loop
7
Assuming 'section_code' is >= field 'next_ordered_section_'
403 section_code < kFirstUnorderedSection) {
404 errorf(pc(), "unexpected section <%s>", SectionName(section_code));
405 return;
406 }
407
408 switch (section_code) {
8
Control jumps to the 'default' case at line 457
409 case kUnknownSectionCode:
410 break;
411 case kDataCountSectionCode:
412 if (!CheckUnorderedSection(section_code)) return;
413 // If wasm-gc is enabled, we allow the data cound section anywhere in
414 // the module.
415 if (!enabled_features_.has_gc() &&
416 !CheckSectionOrder(section_code, kElementSectionCode,
417 kCodeSectionCode)) {
418 return;
419 }
420 break;
421 case kTagSectionCode:
422 if (!CheckUnorderedSection(section_code)) return;
423 if (!CheckSectionOrder(section_code, kMemorySectionCode,
424 kGlobalSectionCode)) {
425 return;
426 }
427 break;
428 case kNameSectionCode:
429 // TODO(titzer): report out of place name section as a warning.
430 // Be lenient with placement of name section. All except first
431 // occurrence are ignored.
432 case kSourceMappingURLSectionCode:
433 // sourceMappingURL is a custom section and currently can occur anywhere
434 // in the module. In case of multiple sourceMappingURL sections, all
435 // except the first occurrence are ignored.
436 case kDebugInfoSectionCode:
437 // .debug_info is a custom section containing core DWARF information
438 // if produced by compiler. Its presence likely means that Wasm was
439 // built in a debug mode.
440 case kExternalDebugInfoSectionCode:
441 // external_debug_info is a custom section containing a reference to an
442 // external symbol file.
443 case kCompilationHintsSectionCode:
444 // TODO(frgossen): report out of place compilation hints section as a
445 // warning.
446 // Be lenient with placement of compilation hints section. All except
447 // first occurrence after function section and before code section are
448 // ignored.
449 break;
450 case kBranchHintsSectionCode:
451 // TODO(yuri): report out of place branch hints section as a
452 // warning.
453 // Be lenient with placement of compilation hints section. All except
454 // first occurrence after function section and before code section are
455 // ignored.
456 break;
457 default:
458 next_ordered_section_ = section_code + 1;
459 break;
460 }
461
462 switch (section_code) {
9
Execution continues on line 462
10
Control jumps to 'case kTypeSectionCode:' at line 465
463 case kUnknownSectionCode:
464 break;
465 case kTypeSectionCode:
466 DecodeTypeSection();
11
Calling 'ModuleDecoderImpl::DecodeTypeSection'
467 break;
468 case kImportSectionCode:
469 DecodeImportSection();
470 break;
471 case kFunctionSectionCode:
472 DecodeFunctionSection();
473 break;
474 case kTableSectionCode:
475 DecodeTableSection();
476 break;
477 case kMemorySectionCode:
478 DecodeMemorySection();
479 break;
480 case kGlobalSectionCode:
481 DecodeGlobalSection();
482 break;
483 case kExportSectionCode:
484 DecodeExportSection();
485 break;
486 case kStartSectionCode:
487 DecodeStartSection();
488 break;
489 case kCodeSectionCode:
490 DecodeCodeSection(verify_functions);
491 break;
492 case kElementSectionCode:
493 DecodeElementSection();
494 break;
495 case kDataSectionCode:
496 DecodeDataSection();
497 break;
498 case kNameSectionCode:
499 DecodeNameSection();
500 break;
501 case kSourceMappingURLSectionCode:
502 DecodeSourceMappingURLSection();
503 break;
504 case kDebugInfoSectionCode:
505 // If there is an explicit source map, prefer it over DWARF info.
506 if (module_->debug_symbols.type == WasmDebugSymbols::Type::None) {
507 module_->debug_symbols = {WasmDebugSymbols::Type::EmbeddedDWARF, {}};
508 }
509 consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info");
510 break;
511 case kExternalDebugInfoSectionCode:
512 DecodeExternalDebugInfoSection();
513 break;
514 case kCompilationHintsSectionCode:
515 if (enabled_features_.has_compilation_hints()) {
516 DecodeCompilationHintsSection();
517 } else {
518 // Ignore this section when feature was disabled. It is an optional
519 // custom section anyways.
520 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
521 }
522 break;
523 case kBranchHintsSectionCode:
524 if (enabled_features_.has_branch_hinting()) {
525 DecodeBranchHintsSection();
526 } else {
527 // Ignore this section when feature was disabled. It is an optional
528 // custom section anyways.
529 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
530 }
531 break;
532 case kDataCountSectionCode:
533 DecodeDataCountSection();
534 break;
535 case kTagSectionCode:
536 if (enabled_features_.has_eh()) {
537 DecodeTagSection();
538 } else {
539 errorf(pc(),
540 "unexpected section <%s> (enable with --experimental-wasm-eh)",
541 SectionName(section_code));
542 }
543 break;
544 default:
545 errorf(pc(), "unexpected section <%s>", SectionName(section_code));
546 return;
547 }
548
549 if (pc() != bytes.end()) {
550 const char* msg = pc() < bytes.end() ? "shorter" : "longer";
551 errorf(pc(),
552 "section was %s than expected size "
553 "(%zu bytes expected, %zu decoded)",
554 msg, bytes.size(), static_cast<size_t>(pc() - bytes.begin()));
555 }
556 }
557
558 TypeDefinition consume_base_type_definition() {
559 DCHECK(enabled_features_.has_gc())((void) 0);
560 uint8_t kind = consume_u8("type kind");
561 switch (kind) {
562 case kWasmFunctionTypeCode: {
563 const FunctionSig* sig = consume_sig(module_->signature_zone.get());
564 return {sig, kNoSuperType};
565 }
566 case kWasmStructTypeCode: {
567 const StructType* type = consume_struct(module_->signature_zone.get());
568 return {type, kNoSuperType};
569 }
570 case kWasmArrayTypeCode: {
571 const ArrayType* type = consume_array(module_->signature_zone.get());
572 return {type, kNoSuperType};
573 }
574 case kWasmFunctionNominalCode:
575 case kWasmArrayNominalCode:
576 case kWasmStructNominalCode:
577 errorf(pc() - 1,
578 "mixing nominal and isorecursive types is not allowed");
579 return {};
580 default:
581 errorf(pc() - 1, "unknown type form: %d", kind);
582 return {};
583 }
584 }
585
586 bool check_supertype(uint32_t supertype) {
587 if (V8_UNLIKELY(supertype >= module_->types.size())(__builtin_expect(!!(supertype >= module_->types.size()
), 0))
) {
588 errorf(pc(), "type %zu: forward-declared supertype %d",
589 module_->types.size(), supertype);
590 return false;
591 }
592 return true;
593 }
594
595 TypeDefinition consume_nominal_type_definition() {
596 DCHECK(enabled_features_.has_gc())((void) 0);
597 size_t num_types = module_->types.size();
598 uint8_t kind = consume_u8("type kind");
599 switch (kind) {
600 case kWasmFunctionNominalCode: {
601 const FunctionSig* sig = consume_sig(module_->signature_zone.get());
602 uint32_t super_index = kNoSuperType;
603 HeapType super_type = consume_super_type();
604 if (super_type.is_index()) {
605 super_index = super_type.representation();
606 } else if (V8_UNLIKELY(super_type != HeapType::kFunc)(__builtin_expect(!!(super_type != HeapType::kFunc), 0))) {
607 errorf(pc() - 1, "type %zu: invalid supertype %d", num_types,
608 super_type.code());
609 return {};
610 }
611 return {sig, super_index};
612 }
613 case kWasmStructNominalCode: {
614 const StructType* type = consume_struct(module_->signature_zone.get());
615 uint32_t super_index = kNoSuperType;
616 HeapType super_type = consume_super_type();
617 if (super_type.is_index()) {
618 super_index = super_type.representation();
619 } else if (V8_UNLIKELY(super_type != HeapType::kData)(__builtin_expect(!!(super_type != HeapType::kData), 0))) {
620 errorf(pc() - 1, "type %zu: invalid supertype %d", num_types,
621 super_type.code());
622 return {};
623 }
624 return {type, super_index};
625 }
626 case kWasmArrayNominalCode: {
627 const ArrayType* type = consume_array(module_->signature_zone.get());
628 uint32_t super_index = kNoSuperType;
629 HeapType super_type = consume_super_type();
630 if (super_type.is_index()) {
631 super_index = super_type.representation();
632 } else if (V8_UNLIKELY(super_type != HeapType::kData)(__builtin_expect(!!(super_type != HeapType::kData), 0))) {
633 errorf(pc() - 1, "type %zu: invalid supertype %d", num_types,
634 super_type.code());
635 return {};
636 }
637 return {type, super_index};
638 }
639 case kWasmFunctionTypeCode:
640 case kWasmArrayTypeCode:
641 case kWasmStructTypeCode:
642 case kWasmSubtypeCode:
643 case kWasmRecursiveTypeGroupCode:
644 errorf(pc() - 1,
645 "mixing nominal and isorecursive types is not allowed");
646 return {};
647 default:
648 errorf(pc() - 1, "unknown type form: %d", kind);
649 return {};
650 }
651 }
652
653 TypeDefinition consume_subtype_definition() {
654 DCHECK(enabled_features_.has_gc())((void) 0);
655 uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind");
656 if (kind == kWasmSubtypeCode) {
657 consume_bytes(1, "subtype definition");
658 constexpr uint32_t kMaximumSupertypes = 1;
659 uint32_t supertype_count =
660 consume_count("supertype count", kMaximumSupertypes);
661 uint32_t supertype =
662 supertype_count == 1 ? consume_u32v("supertype") : kNoSuperType;
663 if (!check_supertype(supertype)) return {};
664 TypeDefinition type = consume_base_type_definition();
665 type.supertype = supertype;
666 return type;
667 } else {
668 return consume_base_type_definition();
669 }
670 }
671
672 void DecodeTypeSection() {
673 TypeCanonicalizer* type_canon = GetTypeCanonicalizer();
674 uint32_t types_count = consume_count("types count", kV8MaxWasmTypes);
675
676 // Non wasm-gc type section decoding.
677 if (!enabled_features_.has_gc()) {
12
Taking false branch
678 module_->types.reserve(types_count);
679 for (uint32_t i = 0; i < types_count; ++i) {
680 TRACE("DecodeSignature[%d] module+%d\n", i,
681 static_cast<int>(pc_ - start_));
682 expect_u8("signature definition", kWasmFunctionTypeCode);
683 const FunctionSig* sig = consume_sig(module_->signature_zone.get());
684 if (!ok()) break;
685 module_->add_signature(sig, kNoSuperType);
686 if (FLAG_wasm_type_canonicalization) {
687 type_canon->AddRecursiveGroup(module_.get(), 1);
688 }
689 }
690 return;
691 }
692
693 if (types_count > 0) {
13
Assuming 'types_count' is > 0
14
Taking true branch
694 uint8_t first_type_opcode = this->read_u8<Decoder::kFullValidation>(pc());
695 if (first_type_opcode == kWasmFunctionNominalCode ||
15
Assuming 'first_type_opcode' is equal to 'kWasmFunctionNominalCode'
696 first_type_opcode == kWasmStructNominalCode ||
697 first_type_opcode == kWasmArrayNominalCode) {
698 // wasm-gc nominal type section decoding.
699 // In a nominal module, all types belong in the same recursive group. We
700 // use the type vector's capacity to mark the end of the current
701 // recursive group.
702 module_->types.reserve(types_count);
703 for (uint32_t i = 0; ok() && i
16.1
'i' is < 'types_count'
16.1
'i' is < 'types_count'
< types_count; ++i) {
16
Assuming the condition is true
17
Loop condition is true. Entering loop body
704 TRACE("DecodeType[%d] module+%d\n", i,
18
Taking false branch
19
Loop condition is false. Exiting loop
705 static_cast<int>(pc_ - start_));
706 TypeDefinition type = consume_nominal_type_definition();
707 if (ok()) module_->add_type(type);
20
Assuming the condition is true
21
Taking true branch
22
Null pointer value stored to 'type..function_sig'
23
Calling 'WasmModule::add_type'
708 }
709 if (ok() && FLAG_wasm_type_canonicalization) {
710 type_canon->AddRecursiveGroup(module_.get(), types_count);
711 }
712 } else {
713 // wasm-gc isorecursive type section decoding.
714 for (uint32_t i = 0; ok() && i < types_count; ++i) {
715 TRACE("DecodeType[%d] module+%d\n", i,
716 static_cast<int>(pc_ - start_));
717 uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind");
718 if (kind == kWasmRecursiveTypeGroupCode) {
719 consume_bytes(1, "rec. group definition");
720 uint32_t group_size =
721 consume_count("recursive group size", kV8MaxWasmTypes);
722 if (module_->types.size() + group_size > kV8MaxWasmTypes) {
723 errorf(pc(), "Type definition count exeeds maximum %zu",
724 kV8MaxWasmTypes);
725 return;
726 }
727 // Reserve space for the current recursive group, so we are
728 // allowed to reference its elements.
729 module_->types.reserve(module_->types.size() + group_size);
730 for (uint32_t i = 0; i < group_size; i++) {
731 TypeDefinition type = consume_subtype_definition();
732 if (ok()) module_->add_type(type);
733 }
734 if (ok() && FLAG_wasm_type_canonicalization) {
735 type_canon->AddRecursiveGroup(module_.get(), group_size);
736 }
737 } else {
738 TypeDefinition type = consume_subtype_definition();
739 if (ok()) {
740 module_->add_type(type);
741 if (FLAG_wasm_type_canonicalization) {
742 type_canon->AddRecursiveGroup(module_.get(), 1);
743 }
744 }
745 }
746 }
747 }
748 }
749
750 // Check validity of explicitly defined supertypes.
751 const WasmModule* module = module_.get();
752 for (uint32_t i = 0; ok() && i < types_count; ++i) {
753 uint32_t explicit_super = module_->supertype(i);
754 if (explicit_super == kNoSuperType) continue;
755 DCHECK_LT(explicit_super, types_count)((void) 0); // {consume_super_type} checks.
756 int depth = GetSubtypingDepth(module, i);
757 if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) {
758 errorf("type %d: subtyping depth is greater than allowed", i);
759 continue;
760 }
761 // TODO(7748): Replace this with a DCHECK once we reject inheritance
762 // cycles for nominal modules.
763 if (depth == -1) {
764 errorf("type %d: cyclic inheritance", i);
765 continue;
766 }
767 if (!ValidSubtypeDefinition(i, explicit_super, module, module)) {
768 errorf("type %d has invalid explicit supertype %d", i, explicit_super);
769 continue;
770 }
771 }
772 module_->signature_map.Freeze();
773 }
774
775 void DecodeImportSection() {
776 uint32_t import_table_count =
777 consume_count("imports count", kV8MaxWasmImports);
778 module_->import_table.reserve(import_table_count);
779 for (uint32_t i = 0; ok() && i < import_table_count; ++i) {
780 TRACE("DecodeImportTable[%d] module+%d\n", i,
781 static_cast<int>(pc_ - start_));
782
783 module_->import_table.push_back({
784 {0, 0}, // module_name
785 {0, 0}, // field_name
786 kExternalFunction, // kind
787 0 // index
788 });
789 WasmImport* import = &module_->import_table.back();
790 const byte* pos = pc_;
791 import->module_name = consume_string(this, true, "module name");
792 import->field_name = consume_string(this, true, "field name");
793 import->kind =
794 static_cast<ImportExportKindCode>(consume_u8("import kind"));
795 switch (import->kind) {
796 case kExternalFunction: {
797 // ===== Imported function ===========================================
798 import->index = static_cast<uint32_t>(module_->functions.size());
799 module_->num_imported_functions++;
800 module_->functions.push_back({nullptr, // sig
801 import->index, // func_index
802 0, // sig_index
803 {0, 0}, // code
804 0, // feedback slots
805 true, // imported
806 false, // exported
807 false}); // declared
808 WasmFunction* function = &module_->functions.back();
809 function->sig_index =
810 consume_sig_index(module_.get(), &function->sig);
811 break;
812 }
813 case kExternalTable: {
814 // ===== Imported table ==============================================
815 import->index = static_cast<uint32_t>(module_->tables.size());
816 module_->num_imported_tables++;
817 module_->tables.emplace_back();
818 WasmTable* table = &module_->tables.back();
819 table->imported = true;
820 const byte* type_position = pc();
821 ValueType type = consume_reference_type();
822 if (!WasmTable::IsValidTableType(type, module_.get())) {
823 errorf(type_position, "Invalid table type %s", type.name().c_str());
824 break;
825 }
826 table->type = type;
827 uint8_t flags = validate_table_flags("element count");
828 consume_resizable_limits(
829 "element count", "elements", std::numeric_limits<uint32_t>::max(),
830 &table->initial_size, &table->has_maximum_size,
831 std::numeric_limits<uint32_t>::max(), &table->maximum_size,
832 flags);
833 break;
834 }
835 case kExternalMemory: {
836 // ===== Imported memory =============================================
837 if (!AddMemory(module_.get())) break;
838 uint8_t flags = validate_memory_flags(&module_->has_shared_memory,
839 &module_->is_memory64);
840 consume_resizable_limits(
841 "memory", "pages", kSpecMaxMemoryPages, &module_->initial_pages,
842 &module_->has_maximum_pages, kSpecMaxMemoryPages,
843 &module_->maximum_pages, flags);
844 break;
845 }
846 case kExternalGlobal: {
847 // ===== Imported global =============================================
848 import->index = static_cast<uint32_t>(module_->globals.size());
849 module_->globals.push_back({kWasmVoid, false, {}, {0}, true, false});
850 WasmGlobal* global = &module_->globals.back();
851 global->type = consume_value_type();
852 global->mutability = consume_mutability();
853 if (global->mutability) {
854 module_->num_imported_mutable_globals++;
855 }
856 break;
857 }
858 case kExternalTag: {
859 // ===== Imported tag ================================================
860 if (!enabled_features_.has_eh()) {
861 errorf(pos, "unknown import kind 0x%02x", import->kind);
862 break;
863 }
864 import->index = static_cast<uint32_t>(module_->tags.size());
865 const WasmTagSig* tag_sig = nullptr;
866 consume_exception_attribute(); // Attribute ignored for now.
867 consume_tag_sig_index(module_.get(), &tag_sig);
868 module_->tags.emplace_back(tag_sig);
869 break;
870 }
871 default:
872 errorf(pos, "unknown import kind 0x%02x", import->kind);
873 break;
874 }
875 }
876 }
877
878 void DecodeFunctionSection() {
879 uint32_t functions_count =
880 consume_count("functions count", kV8MaxWasmFunctions);
881 auto counter =
882 SELECT_WASM_COUNTER(GetCounters(), origin_, wasm_functions_per, module)((origin_) == kWasmOrigin ? (GetCounters())->wasm_functions_per_wasm_module
() : (GetCounters())->wasm_functions_per_asm_module())
;
883 counter->AddSample(static_cast<int>(functions_count));
884 DCHECK_EQ(module_->functions.size(), module_->num_imported_functions)((void) 0);
885 uint32_t total_function_count =
886 module_->num_imported_functions + functions_count;
887 module_->functions.reserve(total_function_count);
888 module_->num_declared_functions = functions_count;
889 for (uint32_t i = 0; i < functions_count; ++i) {
890 uint32_t func_index = static_cast<uint32_t>(module_->functions.size());
891 module_->functions.push_back({nullptr, // sig
892 func_index, // func_index
893 0, // sig_index
894 {0, 0}, // code
895 0, // feedback slots
896 false, // imported
897 false, // exported
898 false}); // declared
899 WasmFunction* function = &module_->functions.back();
900 function->sig_index = consume_sig_index(module_.get(), &function->sig);
901 if (!ok()) return;
902 }
903 DCHECK_EQ(module_->functions.size(), total_function_count)((void) 0);
904 }
905
906 void DecodeTableSection() {
907 uint32_t table_count = consume_count("table count", kV8MaxWasmTables);
908
909 for (uint32_t i = 0; ok() && i < table_count; i++) {
910 module_->tables.emplace_back();
911 WasmTable* table = &module_->tables.back();
912 const byte* type_position = pc();
913 ValueType table_type = consume_reference_type();
914 if (!WasmTable::IsValidTableType(table_type, module_.get())) {
915 error(type_position,
916 "Currently, only externref and function references are allowed "
917 "as table types");
918 continue;
919 }
920 table->type = table_type;
921 uint8_t flags = validate_table_flags("table elements");
922 consume_resizable_limits(
923 "table elements", "elements", std::numeric_limits<uint32_t>::max(),
924 &table->initial_size, &table->has_maximum_size,
925 std::numeric_limits<uint32_t>::max(), &table->maximum_size, flags);
926 if (!table_type.is_defaultable()) {
927 table->initial_value = consume_init_expr(module_.get(), table_type);
928 }
929 }
930 }
931
932 void DecodeMemorySection() {
933 uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories);
934
935 for (uint32_t i = 0; ok() && i < memory_count; i++) {
936 if (!AddMemory(module_.get())) break;
937 uint8_t flags = validate_memory_flags(&module_->has_shared_memory,
938 &module_->is_memory64);
939 consume_resizable_limits("memory", "pages", kSpecMaxMemoryPages,
940 &module_->initial_pages,
941 &module_->has_maximum_pages, kSpecMaxMemoryPages,
942 &module_->maximum_pages, flags);
943 }
944 }
945
946 void DecodeGlobalSection() {
947 uint32_t globals_count = consume_count("globals count", kV8MaxWasmGlobals);
948 uint32_t imported_globals = static_cast<uint32_t>(module_->globals.size());
949 // It is important to not resize the globals vector from the beginning,
950 // because we use its current size when decoding the initializer.
951 module_->globals.reserve(imported_globals + globals_count);
952 for (uint32_t i = 0; ok() && i < globals_count; ++i) {
953 TRACE("DecodeGlobal[%d] module+%d\n", i, static_cast<int>(pc_ - start_));
954 ValueType type = consume_value_type();
955 bool mutability = consume_mutability();
956 if (failed()) break;
957 ConstantExpression init = consume_init_expr(module_.get(), type);
958 module_->globals.push_back({type, mutability, init, {0}, false, false});
959 }
960 if (ok()) CalculateGlobalOffsets(module_.get());
961 }
962
963 void DecodeExportSection() {
964 uint32_t export_table_count =
965 consume_count("exports count", kV8MaxWasmExports);
966 module_->export_table.reserve(export_table_count);
967 for (uint32_t i = 0; ok() && i < export_table_count; ++i) {
968 TRACE("DecodeExportTable[%d] module+%d\n", i,
969 static_cast<int>(pc_ - start_));
970
971 module_->export_table.push_back({
972 {0, 0}, // name
973 kExternalFunction, // kind
974 0 // index
975 });
976 WasmExport* exp = &module_->export_table.back();
977
978 exp->name = consume_string(this, true, "field name");
979
980 const byte* pos = pc();
981 exp->kind = static_cast<ImportExportKindCode>(consume_u8("export kind"));
982 switch (exp->kind) {
983 case kExternalFunction: {
984 WasmFunction* func = nullptr;
985 exp->index =
986 consume_func_index(module_.get(), &func, "export function index");
987
988 if (failed()) break;
989 DCHECK_NOT_NULL(func)((void) 0);
990
991 module_->num_exported_functions++;
992 func->exported = true;
993 // Exported functions are considered "declared".
994 func->declared = true;
995 break;
996 }
997 case kExternalTable: {
998 WasmTable* table = nullptr;
999 exp->index = consume_table_index(module_.get(), &table);
1000 if (table) table->exported = true;
1001 break;
1002 }
1003 case kExternalMemory: {
1004 uint32_t index = consume_u32v("memory index");
1005 // TODO(titzer): This should become more regular
1006 // once we support multiple memories.
1007 if (!module_->has_memory || index != 0) {
1008 error("invalid memory index != 0");
1009 }
1010 module_->mem_export = true;
1011 break;
1012 }
1013 case kExternalGlobal: {
1014 WasmGlobal* global = nullptr;
1015 exp->index = consume_global_index(module_.get(), &global);
1016 if (global) {
1017 global->exported = true;
1018 }
1019 break;
1020 }
1021 case kExternalTag: {
1022 if (!enabled_features_.has_eh()) {
1023 errorf(pos, "invalid export kind 0x%02x", exp->kind);
1024 break;
1025 }
1026 WasmTag* tag = nullptr;
1027 exp->index = consume_tag_index(module_.get(), &tag);
1028 break;
1029 }
1030 default:
1031 errorf(pos, "invalid export kind 0x%02x", exp->kind);
1032 break;
1033 }
1034 }
1035 // Check for duplicate exports (except for asm.js).
1036 if (ok() && origin_ == kWasmOrigin && module_->export_table.size() > 1) {
1037 std::vector<WasmExport> sorted_exports(module_->export_table);
1038
1039 auto cmp_less = [this](const WasmExport& a, const WasmExport& b) {
1040 // Return true if a < b.
1041 if (a.name.length() != b.name.length()) {
1042 return a.name.length() < b.name.length();
1043 }
1044 const byte* left = start() + GetBufferRelativeOffset(a.name.offset());
1045 const byte* right = start() + GetBufferRelativeOffset(b.name.offset());
1046 return memcmp(left, right, a.name.length()) < 0;
1047 };
1048 std::stable_sort(sorted_exports.begin(), sorted_exports.end(), cmp_less);
1049
1050 auto it = sorted_exports.begin();
1051 WasmExport* last = &*it++;
1052 for (auto end = sorted_exports.end(); it != end; last = &*it++) {
1053 DCHECK(!cmp_less(*it, *last))((void) 0); // Vector must be sorted.
1054 if (!cmp_less(*last, *it)) {
1055 const byte* pc = start() + GetBufferRelativeOffset(it->name.offset());
1056 TruncatedUserString<> name(pc, it->name.length());
1057 errorf(pc, "Duplicate export name '%.*s' for %s %d and %s %d",
1058 name.length(), name.start(), ExternalKindName(last->kind),
1059 last->index, ExternalKindName(it->kind), it->index);
1060 break;
1061 }
1062 }
1063 }
1064 }
1065
1066 void DecodeStartSection() {
1067 WasmFunction* func;
1068 const byte* pos = pc_;
1069 module_->start_function_index =
1070 consume_func_index(module_.get(), &func, "start function index");
1071 if (func &&
1072 (func->sig->parameter_count() > 0 || func->sig->return_count() > 0)) {
1073 error(pos, "invalid start function: non-zero parameter or return count");
1074 }
1075 }
1076
1077 void DecodeElementSection() {
1078 uint32_t element_count =
1079 consume_count("element count", FLAG_wasm_max_table_size);
1080
1081 for (uint32_t i = 0; i < element_count; ++i) {
1082 WasmElemSegment segment = consume_element_segment_header();
1083 if (failed()) return;
1084 DCHECK_NE(segment.type, kWasmBottom)((void) 0);
1085
1086 uint32_t num_elem =
1087 consume_count("number of elements", max_table_init_entries());
1088
1089 for (uint32_t j = 0; j < num_elem; j++) {
1090 ConstantExpression entry =
1091 segment.element_type == WasmElemSegment::kExpressionElements
1092 ? consume_init_expr(module_.get(), segment.type)
1093 : ConstantExpression::RefFunc(
1094 consume_element_func_index(segment.type));
1095 if (failed()) return;
1096 segment.entries.push_back(entry);
1097 }
1098 module_->elem_segments.push_back(std::move(segment));
1099 }
1100 }
1101
1102 void DecodeCodeSection(bool verify_functions) {
1103 StartCodeSection();
1104 uint32_t code_section_start = pc_offset();
1105 uint32_t functions_count = consume_u32v("functions count");
1106 CheckFunctionsCount(functions_count, code_section_start);
1107 for (uint32_t i = 0; ok() && i < functions_count; ++i) {
1108 const byte* pos = pc();
1109 uint32_t size = consume_u32v("body size");
1110 if (size > kV8MaxWasmFunctionSize) {
1111 errorf(pos, "size %u > maximum function size %zu", size,
1112 kV8MaxWasmFunctionSize);
1113 return;
1114 }
1115 uint32_t offset = pc_offset();
1116 consume_bytes(size, "function body");
1117 if (failed()) break;
1118 DecodeFunctionBody(i, size, offset, verify_functions);
1119 }
1120 DCHECK_GE(pc_offset(), code_section_start)((void) 0);
1121 set_code_section(code_section_start, pc_offset() - code_section_start);
1122 }
1123
1124 void StartCodeSection() {
1125 if (ok()) {
1126 // Make sure global offset were calculated before they get accessed during
1127 // function compilation.
1128 CalculateGlobalOffsets(module_.get());
1129 }
1130 }
1131
1132 bool CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset) {
1133 if (functions_count != module_->num_declared_functions) {
1134 errorf(error_offset, "function body count %u mismatch (%u expected)",
1135 functions_count, module_->num_declared_functions);
1136 return false;
1137 }
1138 return true;
1139 }
1140
1141 void DecodeFunctionBody(uint32_t index, uint32_t length, uint32_t offset,
1142 bool verify_functions) {
1143 WasmFunction* function =
1144 &module_->functions[index + module_->num_imported_functions];
1145 function->code = {offset, length};
1146 if (verify_functions) {
1147 ModuleWireBytes bytes(module_start_, module_end_);
1148 VerifyFunctionBody(module_->signature_zone->allocator(),
1149 index + module_->num_imported_functions, bytes,
1150 module_.get(), function);
1151 }
1152 }
1153
1154 bool CheckDataSegmentsCount(uint32_t data_segments_count) {
1155 if (has_seen_unordered_section(kDataCountSectionCode) &&
1156 data_segments_count != module_->num_declared_data_segments) {
1157 errorf(pc(), "data segments count %u mismatch (%u expected)",
1158 data_segments_count, module_->num_declared_data_segments);
1159 return false;
1160 }
1161 return true;
1162 }
1163
1164 void DecodeDataSection() {
1165 uint32_t data_segments_count =
1166 consume_count("data segments count", kV8MaxWasmDataSegments);
1167 if (!CheckDataSegmentsCount(data_segments_count)) return;
1168
1169 module_->data_segments.reserve(data_segments_count);
1170 for (uint32_t i = 0; ok() && i < data_segments_count; ++i) {
1171 const byte* pos = pc();
1172 TRACE("DecodeDataSegment[%d] module+%d\n", i,
1173 static_cast<int>(pc_ - start_));
1174
1175 bool is_active;
1176 uint32_t memory_index;
1177 ConstantExpression dest_addr;
1178 consume_data_segment_header(&is_active, &memory_index, &dest_addr);
1179 if (failed()) break;
1180
1181 if (is_active) {
1182 if (!module_->has_memory) {
1183 error("cannot load data without memory");
1184 break;
1185 }
1186 if (memory_index != 0) {
1187 errorf(pos, "illegal memory index %u != 0", memory_index);
1188 break;
1189 }
1190 }
1191
1192 uint32_t source_length = consume_u32v("source size");
1193 uint32_t source_offset = pc_offset();
1194
1195 if (is_active) {
1196 module_->data_segments.emplace_back(std::move(dest_addr));
1197 } else {
1198 module_->data_segments.emplace_back();
1199 }
1200
1201 WasmDataSegment* segment = &module_->data_segments.back();
1202
1203 consume_bytes(source_length, "segment data");
1204 if (failed()) break;
1205
1206 segment->source = {source_offset, source_length};
1207 }
1208 }
1209
1210 void DecodeNameSection() {
1211 // TODO(titzer): find a way to report name errors as warnings.
1212 // Ignore all but the first occurrence of name section.
1213 if (!has_seen_unordered_section(kNameSectionCode)) {
1214 set_seen_unordered_section(kNameSectionCode);
1215 // Use an inner decoder so that errors don't fail the outer decoder.
1216 Decoder inner(start_, pc_, end_, buffer_offset_);
1217 // Decode all name subsections.
1218 // Be lenient with their order.
1219 while (inner.ok() && inner.more()) {
1220 uint8_t name_type = inner.consume_u8("name type");
1221 if (name_type & 0x80) inner.error("name type if not varuint7");
1222
1223 uint32_t name_payload_len = inner.consume_u32v("name payload length");
1224 if (!inner.checkAvailable(name_payload_len)) break;
1225
1226 // Decode module name, ignore the rest.
1227 // Function and local names will be decoded when needed.
1228 if (name_type == NameSectionKindCode::kModuleCode) {
1229 WireBytesRef name = consume_string(&inner, false, "module name");
1230 if (inner.ok() && validate_utf8(&inner, name)) {
1231 module_->name = name;
1232 }
1233 } else {
1234 inner.consume_bytes(name_payload_len, "name subsection payload");
1235 }
1236 }
1237 }
1238 // Skip the whole names section in the outer decoder.
1239 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1240 }
1241
1242 void DecodeSourceMappingURLSection() {
1243 Decoder inner(start_, pc_, end_, buffer_offset_);
1244 WireBytesRef url = wasm::consume_string(&inner, true, "module name");
1245 if (inner.ok() &&
1246 module_->debug_symbols.type != WasmDebugSymbols::Type::SourceMap) {
1247 module_->debug_symbols = {WasmDebugSymbols::Type::SourceMap, url};
1248 }
1249 set_seen_unordered_section(kSourceMappingURLSectionCode);
1250 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1251 }
1252
1253 void DecodeExternalDebugInfoSection() {
1254 Decoder inner(start_, pc_, end_, buffer_offset_);
1255 WireBytesRef url =
1256 wasm::consume_string(&inner, true, "external symbol file");
1257 // If there is an explicit source map, prefer it over DWARF info.
1258 if (inner.ok() &&
1259 module_->debug_symbols.type != WasmDebugSymbols::Type::SourceMap) {
1260 module_->debug_symbols = {WasmDebugSymbols::Type::ExternalDWARF, url};
1261 set_seen_unordered_section(kExternalDebugInfoSectionCode);
1262 }
1263 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1264 }
1265
1266 void DecodeCompilationHintsSection() {
1267 TRACE("DecodeCompilationHints module+%d\n", static_cast<int>(pc_ - start_));
1268
1269 // TODO(frgossen): Find a way to report compilation hint errors as warnings.
1270 // All except first occurrence after function section and before code
1271 // section are ignored.
1272 const bool before_function_section =
1273 next_ordered_section_ <= kFunctionSectionCode;
1274 const bool after_code_section = next_ordered_section_ > kCodeSectionCode;
1275 if (before_function_section || after_code_section ||
1276 has_seen_unordered_section(kCompilationHintsSectionCode)) {
1277 return;
1278 }
1279 set_seen_unordered_section(kCompilationHintsSectionCode);
1280
1281 // TODO(frgossen) Propagate errors to outer decoder in experimental phase.
1282 // We should use an inner decoder later and propagate its errors as
1283 // warnings.
1284 Decoder& decoder = *this;
1285 // Decoder decoder(start_, pc_, end_, buffer_offset_);
1286
1287 // Ensure exactly one compilation hint per function.
1288 uint32_t hint_count = decoder.consume_u32v("compilation hint count");
1289 if (hint_count != module_->num_declared_functions) {
1290 decoder.errorf(decoder.pc(), "Expected %u compilation hints (%u found)",
1291 module_->num_declared_functions, hint_count);
1292 }
1293
1294 // Decode sequence of compilation hints.
1295 if (decoder.ok()) {
1296 module_->compilation_hints.reserve(hint_count);
1297 }
1298 for (uint32_t i = 0; decoder.ok() && i < hint_count; i++) {
1299 TRACE("DecodeCompilationHints[%d] module+%d\n", i,
1300 static_cast<int>(pc_ - start_));
1301
1302 // Compilation hints are encoded in one byte each.
1303 // +-------+----------+---------------+----------+
1304 // | 2 bit | 2 bit | 2 bit | 2 bit |
1305 // | ... | Top tier | Baseline tier | Strategy |
1306 // +-------+----------+---------------+----------+
1307 uint8_t hint_byte = decoder.consume_u8("compilation hint");
1308 if (!decoder.ok()) break;
1309
1310 // Validate the hint_byte.
1311 // For the compilation strategy, all 2-bit values are valid. For the tier,
1312 // only 0x0, 0x1, and 0x2 are allowed.
1313 static_assert(
1314 static_cast<int>(WasmCompilationHintTier::kDefault) == 0 &&
1315 static_cast<int>(WasmCompilationHintTier::kBaseline) == 1 &&
1316 static_cast<int>(WasmCompilationHintTier::kOptimized) == 2,
1317 "The check below assumes that 0x03 is the only invalid 2-bit number "
1318 "for a compilation tier");
1319 if (((hint_byte >> 2) & 0x03) == 0x03 ||
1320 ((hint_byte >> 4) & 0x03) == 0x03) {
1321 decoder.errorf(decoder.pc(),
1322 "Invalid compilation hint %#04x (invalid tier 0x03)",
1323 hint_byte);
1324 break;
1325 }
1326
1327 // Decode compilation hint.
1328 WasmCompilationHint hint;
1329 hint.strategy =
1330 static_cast<WasmCompilationHintStrategy>(hint_byte & 0x03);
1331 hint.baseline_tier =
1332 static_cast<WasmCompilationHintTier>((hint_byte >> 2) & 0x03);
1333 hint.top_tier =
1334 static_cast<WasmCompilationHintTier>((hint_byte >> 4) & 0x03);
1335
1336 // Ensure that the top tier never downgrades a compilation result. If
1337 // baseline and top tier are the same compilation will be invoked only
1338 // once.
1339 if (hint.top_tier < hint.baseline_tier &&
1340 hint.top_tier != WasmCompilationHintTier::kDefault) {
1341 decoder.errorf(decoder.pc(),
1342 "Invalid compilation hint %#04x (forbidden downgrade)",
1343 hint_byte);
1344 }
1345
1346 // Happily accept compilation hint.
1347 if (decoder.ok()) {
1348 module_->compilation_hints.push_back(std::move(hint));
1349 }
1350 }
1351
1352 // If section was invalid reset compilation hints.
1353 if (decoder.failed()) {
1354 module_->compilation_hints.clear();
1355 }
1356
1357 // @TODO(frgossen) Skip the whole compilation hints section in the outer
1358 // decoder if inner decoder was used.
1359 // consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1360 }
1361
1362 void DecodeBranchHintsSection() {
1363 TRACE("DecodeBranchHints module+%d\n", static_cast<int>(pc_ - start_));
1364 if (!has_seen_unordered_section(kBranchHintsSectionCode)) {
1365 set_seen_unordered_section(kBranchHintsSectionCode);
1366 // Use an inner decoder so that errors don't fail the outer decoder.
1367 Decoder inner(start_, pc_, end_, buffer_offset_);
1368 BranchHintInfo branch_hints;
1369
1370 uint32_t func_count = inner.consume_u32v("number of functions");
1371 // Keep track of the previous function index to validate the ordering
1372 int64_t last_func_idx = -1;
1373 for (uint32_t i = 0; i < func_count; i++) {
1374 uint32_t func_idx = inner.consume_u32v("function index");
1375 if (int64_t(func_idx) <= last_func_idx) {
1376 inner.errorf("Invalid function index: %d", func_idx);
1377 break;
1378 }
1379 last_func_idx = func_idx;
1380 uint32_t num_hints = inner.consume_u32v("number of hints");
1381 BranchHintMap func_branch_hints;
1382 TRACE("DecodeBranchHints[%d] module+%d\n", func_idx,
1383 static_cast<int>(inner.pc() - inner.start()));
1384 // Keep track of the previous branch offset to validate the ordering
1385 int64_t last_br_off = -1;
1386 for (uint32_t j = 0; j < num_hints; ++j) {
1387 uint32_t br_off = inner.consume_u32v("branch instruction offset");
1388 if (int64_t(br_off) <= last_br_off) {
1389 inner.errorf("Invalid branch offset: %d", br_off);
1390 break;
1391 }
1392 last_br_off = br_off;
1393 uint32_t data_size = inner.consume_u32v("data size");
1394 if (data_size != 1) {
1395 inner.errorf("Invalid data size: %#x. Expected 1.", data_size);
1396 break;
1397 }
1398 uint32_t br_dir = inner.consume_u8("branch direction");
1399 TRACE("DecodeBranchHints[%d][%d] module+%d\n", func_idx, br_off,
1400 static_cast<int>(inner.pc() - inner.start()));
1401 WasmBranchHint hint;
1402 switch (br_dir) {
1403 case 0:
1404 hint = WasmBranchHint::kUnlikely;
1405 break;
1406 case 1:
1407 hint = WasmBranchHint::kLikely;
1408 break;
1409 default:
1410 hint = WasmBranchHint::kNoHint;
1411 inner.errorf(inner.pc(), "Invalid branch hint %#x", br_dir);
1412 break;
1413 }
1414 if (!inner.ok()) {
1415 break;
1416 }
1417 func_branch_hints.insert(br_off, hint);
1418 }
1419 if (!inner.ok()) {
1420 break;
1421 }
1422 branch_hints.emplace(func_idx, std::move(func_branch_hints));
1423 }
1424 // Extra unexpected bytes are an error.
1425 if (inner.more()) {
1426 inner.errorf("Unexpected extra bytes: %d\n",
1427 static_cast<int>(inner.pc() - inner.start()));
1428 }
1429 // If everything went well, accept the hints for the module.
1430 if (inner.ok()) {
1431 module_->branch_hints = std::move(branch_hints);
1432 }
1433 }
1434 // Skip the whole branch hints section in the outer decoder.
1435 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1436 }
1437
1438 void DecodeDataCountSection() {
1439 module_->num_declared_data_segments =
1440 consume_count("data segments count", kV8MaxWasmDataSegments);
1441 }
1442
1443 void DecodeTagSection() {
1444 uint32_t tag_count = consume_count("tag count", kV8MaxWasmTags);
1445 for (uint32_t i = 0; ok() && i < tag_count; ++i) {
1446 TRACE("DecodeTag[%d] module+%d\n", i, static_cast<int>(pc_ - start_));
1447 const WasmTagSig* tag_sig = nullptr;
1448 consume_exception_attribute(); // Attribute ignored for now.
1449 consume_tag_sig_index(module_.get(), &tag_sig);
1450 module_->tags.emplace_back(tag_sig);
1451 }
1452 }
1453
1454 bool CheckMismatchedCounts() {
1455 // The declared vs. defined function count is normally checked when
1456 // decoding the code section, but we have to check it here too in case the
1457 // code section is absent.
1458 if (module_->num_declared_functions != 0) {
1459 DCHECK_LT(module_->num_imported_functions, module_->functions.size())((void) 0);
1460 // We know that the code section has been decoded if the first
1461 // non-imported function has its code set.
1462 if (!module_->functions[module_->num_imported_functions].code.is_set()) {
1463 errorf(pc(), "function count is %u, but code section is absent",
1464 module_->num_declared_functions);
1465 return false;
1466 }
1467 }
1468 // Perform a similar check for the DataCount and Data sections, where data
1469 // segments are declared but the Data section is absent.
1470 if (!CheckDataSegmentsCount(
1471 static_cast<uint32_t>(module_->data_segments.size()))) {
1472 return false;
1473 }
1474 return true;
1475 }
1476
1477 ModuleResult FinishDecoding(bool verify_functions = true) {
1478 if (ok() && CheckMismatchedCounts()) {
1479 // We calculate the global offsets here, because there may not be a
1480 // global section and code section that would have triggered the
1481 // calculation before. Even without the globals section the calculation
1482 // is needed because globals can also be defined in the import section.
1483 CalculateGlobalOffsets(module_.get());
1484 }
1485
1486 ModuleResult result = toResult(std::move(module_));
1487 if (verify_functions && result.ok() && intermediate_error_.has_error()) {
1488 // Copy error message and location.
1489 return ModuleResult{std::move(intermediate_error_)};
1490 }
1491 return result;
1492 }
1493
1494 void set_code_section(uint32_t offset, uint32_t size) {
1495 module_->code = {offset, size};
1496 }
1497
1498 // Decodes an entire module.
1499 ModuleResult DecodeModule(Counters* counters, AccountingAllocator* allocator,
1500 bool verify_functions = true) {
1501 StartDecoding(counters, allocator);
1502 uint32_t offset = 0;
1503 base::Vector<const byte> orig_bytes(start(), end() - start());
1504 DecodeModuleHeader(base::VectorOf(start(), end() - start()), offset);
1505 if (failed()) {
1506 return FinishDecoding(verify_functions);
1507 }
1508 // Size of the module header.
1509 offset += 8;
1510 Decoder decoder(start_ + offset, end_, offset);
1511
1512 WasmSectionIterator section_iter(&decoder);
1513
1514 while (ok()) {
1515 // Shift the offset by the section header length
1516 offset += section_iter.payload_start() - section_iter.section_start();
1517 if (section_iter.section_code() != SectionCode::kUnknownSectionCode) {
1518 DecodeSection(section_iter.section_code(), section_iter.payload(),
1519 offset, verify_functions);
1520 }
1521 // Shift the offset by the remaining section payload
1522 offset += section_iter.payload_length();
1523 if (!section_iter.more()) break;
1524 section_iter.advance(true);
1525 }
1526
1527 if (FLAG_dump_wasm_module) DumpModule(orig_bytes);
1528
1529 if (decoder.failed()) {
1530 return decoder.toResult<std::unique_ptr<WasmModule>>(nullptr);
1531 }
1532
1533 return FinishDecoding(verify_functions);
1534 }
1535
1536 // Decodes a single anonymous function starting at {start_}.
1537 FunctionResult DecodeSingleFunction(Zone* zone,
1538 const ModuleWireBytes& wire_bytes,
1539 const WasmModule* module,
1540 std::unique_ptr<WasmFunction> function) {
1541 pc_ = start_;
1542 expect_u8("type form", kWasmFunctionTypeCode);
1543 if (!ok()) return FunctionResult{std::move(intermediate_error_)};
1544 function->sig = consume_sig(zone);
1545 function->code = {off(pc_), static_cast<uint32_t>(end_ - pc_)};
1546
1547 if (ok())
1548 VerifyFunctionBody(zone->allocator(), 0, wire_bytes, module,
1549 function.get());
1550
1551 if (intermediate_error_.has_error()) {
1552 return FunctionResult{std::move(intermediate_error_)};
1553 }
1554
1555 return FunctionResult(std::move(function));
1556 }
1557
1558 // Decodes a single function signature at {start}.
1559 const FunctionSig* DecodeFunctionSignature(Zone* zone, const byte* start) {
1560 pc_ = start;
1561 if (!expect_u8("type form", kWasmFunctionTypeCode)) return nullptr;
1562 const FunctionSig* result = consume_sig(zone);
1563 return ok() ? result : nullptr;
1564 }
1565
1566 ConstantExpression DecodeInitExprForTesting(ValueType expected) {
1567 return consume_init_expr(module_.get(), expected);
1568 }
1569
1570 const std::shared_ptr<WasmModule>& shared_module() const { return module_; }
1571
1572 Counters* GetCounters() const {
1573 DCHECK_NOT_NULL(counters_)((void) 0);
1574 return counters_;
1575 }
1576
1577 void SetCounters(Counters* counters) {
1578 DCHECK_NULL(counters_)((void) 0);
1579 counters_ = counters;
1580 }
1581
1582 private:
1583 const WasmFeatures enabled_features_;
1584 std::shared_ptr<WasmModule> module_;
1585 const byte* module_start_ = nullptr;
1586 const byte* module_end_ = nullptr;
1587 Counters* counters_ = nullptr;
1588 // The type section is the first section in a module.
1589 uint8_t next_ordered_section_ = kFirstSectionInModule;
1590 // We store next_ordered_section_ as uint8_t instead of SectionCode so that
1591 // we can increment it. This static_assert should make sure that SectionCode
1592 // does not get bigger than uint8_t accidentially.
1593 static_assert(sizeof(ModuleDecoderImpl::next_ordered_section_) ==
1594 sizeof(SectionCode),
1595 "type mismatch");
1596 uint32_t seen_unordered_sections_ = 0;
1597 static_assert(kBitsPerByte *
1598 sizeof(ModuleDecoderImpl::seen_unordered_sections_) >
1599 kLastKnownModuleSection,
1600 "not enough bits");
1601 WasmError intermediate_error_;
1602 ModuleOrigin origin_;
1603 AccountingAllocator allocator_;
1604 Zone init_expr_zone_{&allocator_, "initializer expression zone"};
1605
1606 bool has_seen_unordered_section(SectionCode section_code) {
1607 return seen_unordered_sections_ & (1 << section_code);
1608 }
1609
1610 void set_seen_unordered_section(SectionCode section_code) {
1611 seen_unordered_sections_ |= 1 << section_code;
1612 }
1613
1614 uint32_t off(const byte* ptr) {
1615 return static_cast<uint32_t>(ptr - start_) + buffer_offset_;
1616 }
1617
1618 bool AddMemory(WasmModule* module) {
1619 if (module->has_memory) {
1620 error("At most one memory is supported");
1621 return false;
1622 } else {
1623 module->has_memory = true;
1624 return true;
1625 }
1626 }
1627
1628 // Calculate individual global offsets and total size of globals table. This
1629 // function should be called after all globals have been defined, which is
1630 // after the import section and the global section, but before the global
1631 // offsets are accessed, e.g. by the function compilers. The moment when this
1632 // function should be called is not well-defined, as the global section may
1633 // not exist. Therefore this function is called multiple times.
1634 void CalculateGlobalOffsets(WasmModule* module) {
1635 if (module->globals.empty() || module->untagged_globals_buffer_size != 0 ||
1636 module->tagged_globals_buffer_size != 0) {
1637 // This function has already been executed before, so we don't have to
1638 // execute it again.
1639 return;
1640 }
1641 uint32_t untagged_offset = 0;
1642 uint32_t tagged_offset = 0;
1643 uint32_t num_imported_mutable_globals = 0;
1644 for (WasmGlobal& global : module->globals) {
1645 if (global.mutability && global.imported) {
1646 global.index = num_imported_mutable_globals++;
1647 } else if (global.type.is_reference()) {
1648 global.offset = tagged_offset;
1649 // All entries in the tagged_globals_buffer have size 1.
1650 tagged_offset++;
1651 } else {
1652 int size = global.type.value_kind_size();
1653 untagged_offset = (untagged_offset + size - 1) & ~(size - 1); // align
1654 global.offset = untagged_offset;
1655 untagged_offset += size;
1656 }
1657 }
1658 module->untagged_globals_buffer_size = untagged_offset;
1659 module->tagged_globals_buffer_size = tagged_offset;
1660 }
1661
1662 // Verifies the body (code) of a given function.
1663 void VerifyFunctionBody(AccountingAllocator* allocator, uint32_t func_num,
1664 const ModuleWireBytes& wire_bytes,
1665 const WasmModule* module, WasmFunction* function) {
1666 WasmFunctionName func_name(function,
1667 wire_bytes.GetNameOrNull(function, module));
1668 if (FLAG_trace_wasm_decoder) {
1669 StdoutStream{} << "Verifying wasm function " << func_name << std::endl;
1670 }
1671 FunctionBody body = {
1672 function->sig, function->code.offset(),
1673 start_ + GetBufferRelativeOffset(function->code.offset()),
1674 start_ + GetBufferRelativeOffset(function->code.end_offset())};
1675
1676 WasmFeatures unused_detected_features = WasmFeatures::None();
1677 DecodeResult result = VerifyWasmCode(allocator, enabled_features_, module,
1678 &unused_detected_features, body);
1679
1680 // If the decode failed and this is the first error, set error code and
1681 // location.
1682 if (result.failed() && intermediate_error_.empty()) {
1683 // Wrap the error message from the function decoder.
1684 std::ostringstream error_msg;
1685 error_msg << "in function " << func_name << ": "
1686 << result.error().message();
1687 intermediate_error_ = WasmError{result.error().offset(), error_msg.str()};
1688 }
1689 }
1690
1691 uint32_t consume_sig_index(WasmModule* module, const FunctionSig** sig) {
1692 const byte* pos = pc_;
1693 uint32_t sig_index = consume_u32v("signature index");
1694 if (!module->has_signature(sig_index)) {
1695 errorf(pos, "signature index %u out of bounds (%d signatures)", sig_index,
1696 static_cast<int>(module->types.size()));
1697 *sig = nullptr;
1698 return 0;
1699 }
1700 *sig = module->signature(sig_index);
1701 return sig_index;
1702 }
1703
1704 uint32_t consume_tag_sig_index(WasmModule* module, const FunctionSig** sig) {
1705 const byte* pos = pc_;
1706 uint32_t sig_index = consume_sig_index(module, sig);
1707 if (*sig && (*sig)->return_count() != 0) {
1708 errorf(pos, "tag signature %u has non-void return", sig_index);
1709 *sig = nullptr;
1710 return 0;
1711 }
1712 return sig_index;
1713 }
1714
1715 uint32_t consume_count(const char* name, size_t maximum) {
1716 const byte* p = pc_;
1717 uint32_t count = consume_u32v(name);
1718 if (count > maximum) {
1719 errorf(p, "%s of %u exceeds internal limit of %zu", name, count, maximum);
1720 return static_cast<uint32_t>(maximum);
1721 }
1722 return count;
1723 }
1724
1725 uint32_t consume_func_index(WasmModule* module, WasmFunction** func,
1726 const char* name) {
1727 return consume_index(name, &module->functions, func);
1728 }
1729
1730 uint32_t consume_global_index(WasmModule* module, WasmGlobal** global) {
1731 return consume_index("global index", &module->globals, global);
1732 }
1733
1734 uint32_t consume_table_index(WasmModule* module, WasmTable** table) {
1735 return consume_index("table index", &module->tables, table);
1736 }
1737
1738 uint32_t consume_tag_index(WasmModule* module, WasmTag** tag) {
1739 return consume_index("tag index", &module->tags, tag);
1740 }
1741
1742 template <typename T>
1743 uint32_t consume_index(const char* name, std::vector<T>* vector, T** ptr) {
1744 const byte* pos = pc_;
1745 uint32_t index = consume_u32v(name);
1746 if (index >= vector->size()) {
1747 errorf(pos, "%s %u out of bounds (%d entr%s)", name, index,
1748 static_cast<int>(vector->size()),
1749 vector->size() == 1 ? "y" : "ies");
1750 *ptr = nullptr;
1751 return 0;
1752 }
1753 *ptr = &(*vector)[index];
1754 return index;
1755 }
1756
1757 uint8_t validate_table_flags(const char* name) {
1758 uint8_t flags = consume_u8("table limits flags");
1759 STATIC_ASSERT(kNoMaximum < kWithMaximum)static_assert(kNoMaximum < kWithMaximum, "kNoMaximum < kWithMaximum"
)
;
1760 if (V8_UNLIKELY(flags > kWithMaximum)(__builtin_expect(!!(flags > kWithMaximum), 0))) {
1761 errorf(pc() - 1, "invalid %s limits flags", name);
1762 }
1763 return flags;
1764 }
1765
1766 uint8_t validate_memory_flags(bool* has_shared_memory, bool* is_memory64) {
1767 uint8_t flags = consume_u8("memory limits flags");
1768 *has_shared_memory = false;
1769 switch (flags) {
1770 case kNoMaximum:
1771 case kWithMaximum:
1772 break;
1773 case kSharedNoMaximum:
1774 case kSharedWithMaximum:
1775 if (!enabled_features_.has_threads()) {
1776 errorf(pc() - 1,
1777 "invalid memory limits flags 0x%x (enable via "
1778 "--experimental-wasm-threads)",
1779 flags);
1780 }
1781 *has_shared_memory = true;
1782 // V8 does not support shared memory without a maximum.
1783 if (flags == kSharedNoMaximum) {
1784 errorf(pc() - 1,
1785 "memory limits flags must have maximum defined if shared is "
1786 "true");
1787 }
1788 break;
1789 case kMemory64NoMaximum:
1790 case kMemory64WithMaximum:
1791 if (!enabled_features_.has_memory64()) {
1792 errorf(pc() - 1,
1793 "invalid memory limits flags 0x%x (enable via "
1794 "--experimental-wasm-memory64)",
1795 flags);
1796 }
1797 *is_memory64 = true;
1798 break;
1799 default:
1800 errorf(pc() - 1, "invalid memory limits flags 0x%x", flags);
1801 break;
1802 }
1803 return flags;
1804 }
1805
1806 void consume_resizable_limits(const char* name, const char* units,
1807 uint32_t max_initial, uint32_t* initial,
1808 bool* has_max, uint32_t max_maximum,
1809 uint32_t* maximum, uint8_t flags) {
1810 const byte* pos = pc();
1811 // For memory64 we need to read the numbers as LEB-encoded 64-bit unsigned
1812 // integer. All V8 limits are still within uint32_t range though.
1813 const bool is_memory64 =
1814 flags == kMemory64NoMaximum || flags == kMemory64WithMaximum;
1815 uint64_t initial_64 = is_memory64 ? consume_u64v("initial size")
1816 : consume_u32v("initial size");
1817 if (initial_64 > max_initial) {
1818 errorf(pos,
1819 "initial %s size (%" PRIu64"l" "u"
1820 " %s) is larger than implementation limit (%u)",
1821 name, initial_64, units, max_initial);
1822 }
1823 *initial = static_cast<uint32_t>(initial_64);
1824 if (flags & 1) {
1825 *has_max = true;
1826 pos = pc();
1827 uint64_t maximum_64 = is_memory64 ? consume_u64v("maximum size")
1828 : consume_u32v("maximum size");
1829 if (maximum_64 > max_maximum) {
1830 errorf(pos,
1831 "maximum %s size (%" PRIu64"l" "u"
1832 " %s) is larger than implementation limit (%u)",
1833 name, maximum_64, units, max_maximum);
1834 }
1835 if (maximum_64 < *initial) {
1836 errorf(pos,
1837 "maximum %s size (%" PRIu64"l" "u" " %s) is less than initial (%u %s)",
1838 name, maximum_64, units, *initial, units);
1839 }
1840 *maximum = static_cast<uint32_t>(maximum_64);
1841 } else {
1842 *has_max = false;
1843 *maximum = max_initial;
1844 }
1845 }
1846
1847 // Consumes a byte, and emits an error if it does not equal {expected}.
1848 bool expect_u8(const char* name, uint8_t expected) {
1849 const byte* pos = pc();
1850 uint8_t value = consume_u8(name);
1851 if (value != expected) {
1852 errorf(pos, "expected %s 0x%02x, got 0x%02x", name, expected, value);
1853 return false;
1854 }
1855 return true;
1856 }
1857
1858 ConstantExpression consume_init_expr(WasmModule* module, ValueType expected) {
1859 uint32_t length;
1860
1861 // The error message mimics the one generated by the {WasmFullDecoder}.
1862#define TYPE_CHECK(found) \
1863 if (V8_UNLIKELY(!IsSubtypeOf(found, expected, module_.get()))(__builtin_expect(!!(!IsSubtypeOf(found, expected, module_.get
())), 0))
) { \
1864 errorf(pc() + 1, \
1865 "type error in init. expression[0] (expected %s, got %s)", \
1866 expected.name().c_str(), found.name().c_str()); \
1867 return {}; \
1868 }
1869
1870 // To avoid initializing a {WasmFullDecoder} for the most common
1871 // expressions, we replicate their decoding and validation here. The
1872 // manually handled cases correspond to {ConstantExpression}'s kinds.
1873 // We need to make sure to check that the expression ends in {kExprEnd};
1874 // otherwise, it is just the first operand of a composite expression, and we
1875 // fall back to the default case.
1876 if (!more()) {
1877 error("Beyond end of code");
1878 return {};
1879 }
1880 switch (static_cast<WasmOpcode>(*pc())) {
1881 case kExprI32Const: {
1882 int32_t value =
1883 read_i32v<kFullValidation>(pc() + 1, &length, "i32.const");
1884 if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {};
1885 if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) {
1886 TYPE_CHECK(kWasmI32)
1887 consume_bytes(length + 2);
1888 return ConstantExpression::I32Const(value);
1889 }
1890 break;
1891 }
1892 case kExprRefFunc: {
1893 uint32_t index =
1894 read_u32v<kFullValidation>(pc() + 1, &length, "ref.func");
1895 if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {};
1896 if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) {
1897 if (V8_UNLIKELY(index >= module_->functions.size())(__builtin_expect(!!(index >= module_->functions.size()
), 0))
) {
1898 errorf(pc() + 1, "function index %u out of bounds", index);
1899 return {};
1900 }
1901 ValueType type =
1902 enabled_features_.has_typed_funcref()
1903 ? ValueType::Ref(module_->functions[index].sig_index,
1904 kNonNullable)
1905 : kWasmFuncRef;
1906 TYPE_CHECK(type)
1907 module_->functions[index].declared = true;
1908 consume_bytes(length + 2);
1909 return ConstantExpression::RefFunc(index);
1910 }
1911 break;
1912 }
1913 case kExprRefNull: {
1914 HeapType type = value_type_reader::read_heap_type<kFullValidation>(
1915 this, pc() + 1, &length, module_.get(), enabled_features_);
1916 if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {};
1917 if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) {
1918 TYPE_CHECK(ValueType::Ref(type, kNullable))
1919 consume_bytes(length + 2);
1920 return ConstantExpression::RefNull(type.representation());
1921 }
1922 break;
1923 }
1924 default:
1925 break;
1926 }
1927#undef TYPE_CHECK
1928
1929 auto sig = FixedSizeSignature<ValueType>::Returns(expected);
1930 FunctionBody body(&sig, buffer_offset_, pc_, end_);
1931 WasmFeatures detected;
1932 WasmFullDecoder<Decoder::kFullValidation, InitExprInterface,
1933 kInitExpression>
1934 decoder(&init_expr_zone_, module, enabled_features_, &detected, body,
1935 module);
1936
1937 uint32_t offset = this->pc_offset();
1938
1939 decoder.DecodeFunctionBody();
1940
1941 this->pc_ = decoder.end();
1942
1943 if (decoder.failed()) {
1944 error(decoder.error().offset(), decoder.error().message().c_str());
1945 return {};
1946 }
1947
1948 if (!decoder.interface().end_found()) {
1949 error("Initializer expression is missing 'end'");
1950 return {};
1951 }
1952
1953 return ConstantExpression::WireBytes(
1954 offset, static_cast<uint32_t>(decoder.end() - decoder.start()));
1955 }
1956
1957 // Read a mutability flag
1958 bool consume_mutability() {
1959 byte val = consume_u8("mutability");
1960 if (val > 1) error(pc_ - 1, "invalid mutability");
1961 return val != 0;
1962 }
1963
1964 ValueType consume_value_type() {
1965 uint32_t type_length;
1966 ValueType result = value_type_reader::read_value_type<kFullValidation>(
1967 this, this->pc(), &type_length, module_.get(),
1968 origin_ == kWasmOrigin ? enabled_features_ : WasmFeatures::None());
1969 consume_bytes(type_length, "value type");
1970 return result;
1971 }
1972
1973 HeapType consume_super_type() {
1974 return value_type_reader::consume_heap_type(this, module_.get(),
1975 enabled_features_);
1976 }
1977
1978 ValueType consume_storage_type() {
1979 uint8_t opcode = read_u8<kFullValidation>(this->pc());
1980 switch (opcode) {
1981 case kI8Code:
1982 consume_bytes(1, "i8");
1983 return kWasmI8;
1984 case kI16Code:
1985 consume_bytes(1, "i16");
1986 return kWasmI16;
1987 default:
1988 // It is not a packed type, so it has to be a value type.
1989 return consume_value_type();
1990 }
1991 }
1992
1993 // Reads a reference type for tables and element segment headers.
1994 ValueType consume_reference_type() {
1995 const byte* position = pc();
1996 ValueType result = consume_value_type();
1997 if (!result.is_reference()) {
1998 error(position, "expected reference type");
1999 }
2000 return result;
2001 }
2002
2003 const FunctionSig* consume_sig(Zone* zone) {
2004 // Parse parameter types.
2005 uint32_t param_count =
2006 consume_count("param count", kV8MaxWasmFunctionParams);
2007 if (failed()) return nullptr;
2008 std::vector<ValueType> params;
2009 for (uint32_t i = 0; ok() && i < param_count; ++i) {
2010 params.push_back(consume_value_type());
2011 }
2012 std::vector<ValueType> returns;
2013
2014 // Parse return types.
2015 uint32_t return_count =
2016 consume_count("return count", kV8MaxWasmFunctionReturns);
2017 if (failed()) return nullptr;
2018 for (uint32_t i = 0; ok() && i < return_count; ++i) {
2019 returns.push_back(consume_value_type());
2020 }
2021 if (failed()) return nullptr;
2022
2023 // FunctionSig stores the return types first.
2024 ValueType* buffer = zone->NewArray<ValueType>(param_count + return_count);
2025 uint32_t b = 0;
2026 for (uint32_t i = 0; i < return_count; ++i) buffer[b++] = returns[i];
2027 for (uint32_t i = 0; i < param_count; ++i) buffer[b++] = params[i];
2028
2029 return zone->New<FunctionSig>(return_count, param_count, buffer);
2030 }
2031
2032 const StructType* consume_struct(Zone* zone) {
2033 uint32_t field_count = consume_count("field count", kV8MaxWasmStructFields);
2034 if (failed()) return nullptr;
2035 ValueType* fields = zone->NewArray<ValueType>(field_count);
2036 bool* mutabilities = zone->NewArray<bool>(field_count);
2037 for (uint32_t i = 0; ok() && i < field_count; ++i) {
2038 fields[i] = consume_storage_type();
2039 mutabilities[i] = consume_mutability();
2040 }
2041 if (failed()) return nullptr;
2042 uint32_t* offsets = zone->NewArray<uint32_t>(field_count);
2043 return zone->New<StructType>(field_count, offsets, fields, mutabilities);
2044 }
2045
2046 const ArrayType* consume_array(Zone* zone) {
2047 ValueType element_type = consume_storage_type();
2048 bool mutability = consume_mutability();
2049 if (failed()) return nullptr;
2050 return zone->New<ArrayType>(element_type, mutability);
2051 }
2052
2053 // Consume the attribute field of an exception.
2054 uint32_t consume_exception_attribute() {
2055 const byte* pos = pc_;
2056 uint32_t attribute = consume_u32v("exception attribute");
2057 if (attribute != kExceptionAttribute) {
2058 errorf(pos, "exception attribute %u not supported", attribute);
2059 return 0;
2060 }
2061 return attribute;
2062 }
2063
2064 WasmElemSegment consume_element_segment_header() {
2065 const byte* pos = pc();
2066
2067 // The mask for the bit in the flag which indicates if the segment is
2068 // active or not (0 is active).
2069 constexpr uint8_t kNonActiveMask = 1 << 0;
2070 // The mask for the bit in the flag which indicates:
2071 // - for active tables, if the segment has an explicit table index field.
2072 // - for non-active tables, whether the table is declarative (vs. passive).
2073 constexpr uint8_t kHasTableIndexOrIsDeclarativeMask = 1 << 1;
2074 // The mask for the bit in the flag which indicates if the functions of this
2075 // segment are defined as function indices (0) or init. expressions (1).
2076 constexpr uint8_t kExpressionsAsElementsMask = 1 << 2;
2077 constexpr uint8_t kFullMask = kNonActiveMask |
2078 kHasTableIndexOrIsDeclarativeMask |
2079 kExpressionsAsElementsMask;
2080
2081 uint32_t flag = consume_u32v("flag");
2082 if ((flag & kFullMask) != flag) {
2083 errorf(pos, "illegal flag value %u. Must be between 0 and 7", flag);
2084 return {};
2085 }
2086
2087 const WasmElemSegment::Status status =
2088 (flag & kNonActiveMask) ? (flag & kHasTableIndexOrIsDeclarativeMask)
2089 ? WasmElemSegment::kStatusDeclarative
2090 : WasmElemSegment::kStatusPassive
2091 : WasmElemSegment::kStatusActive;
2092 const bool is_active = status == WasmElemSegment::kStatusActive;
2093
2094 WasmElemSegment::ElementType element_type =
2095 flag & kExpressionsAsElementsMask
2096 ? WasmElemSegment::kExpressionElements
2097 : WasmElemSegment::kFunctionIndexElements;
2098
2099 const bool has_table_index =
2100 is_active && (flag & kHasTableIndexOrIsDeclarativeMask);
2101 uint32_t table_index = has_table_index ? consume_u32v("table index") : 0;
2102 if (is_active && table_index >= module_->tables.size()) {
2103 errorf(pos, "out of bounds%s table index %u",
2104 has_table_index ? " implicit" : "", table_index);
2105 return {};
2106 }
2107 ValueType table_type =
2108 is_active ? module_->tables[table_index].type : kWasmBottom;
2109
2110 ConstantExpression offset;
2111 if (is_active) {
2112 offset = consume_init_expr(module_.get(), kWasmI32);
2113 // Failed to parse offset initializer, return early.
2114 if (failed()) return {};
2115 }
2116
2117 // Denotes an active segment without table index, type, or element kind.
2118 const bool backwards_compatible_mode =
2119 is_active && !(flag & kHasTableIndexOrIsDeclarativeMask);
2120 ValueType type;
2121 if (element_type == WasmElemSegment::kExpressionElements) {
2122 type =
2123 backwards_compatible_mode ? kWasmFuncRef : consume_reference_type();
2124 if (is_active && !IsSubtypeOf(type, table_type, this->module_.get())) {
2125 errorf(pos,
2126 "Element segment of type %s is not a subtype of referenced "
2127 "table %u (of type %s)",
2128 type.name().c_str(), table_index, table_type.name().c_str());
2129 return {};
2130 }
2131 } else {
2132 if (!backwards_compatible_mode) {
2133 // We have to check that there is an element kind of type Function. All
2134 // other element kinds are not valid yet.
2135 uint8_t val = consume_u8("element kind");
2136 if (static_cast<ImportExportKindCode>(val) != kExternalFunction) {
2137 errorf(pos, "illegal element kind 0x%x. Must be 0x%x", val,
2138 kExternalFunction);
2139 return {};
2140 }
2141 }
2142 if (!is_active) {
2143 // Declarative and passive segments without explicit type are funcref.
2144 type = kWasmFuncRef;
2145 } else {
2146 type = table_type;
2147 // Active segments with function indices must reference a function
2148 // table. TODO(7748): Add support for anyref tables when we have them.
2149 if (!IsSubtypeOf(table_type, kWasmFuncRef, this->module_.get())) {
2150 errorf(pos,
2151 "An active element segment with function indices as elements "
2152 "must reference a table of %s. Instead, table %u of type %s "
2153 "is referenced.",
2154 enabled_features_.has_typed_funcref()
2155 ? "a subtype of type funcref"
2156 : "type funcref",
2157 table_index, table_type.name().c_str());
2158 return {};
2159 }
2160 }
2161 }
2162
2163 if (is_active) {
2164 return {type, table_index, std::move(offset), element_type};
2165 } else {
2166 return {type, status, element_type};
2167 }
2168 }
2169
2170 void consume_data_segment_header(bool* is_active, uint32_t* index,
2171 ConstantExpression* offset) {
2172 const byte* pos = pc();
2173 uint32_t flag = consume_u32v("flag");
2174
2175 // Some flag values are only valid for specific proposals.
2176 if (flag != SegmentFlags::kActiveNoIndex &&
2177 flag != SegmentFlags::kPassive &&
2178 flag != SegmentFlags::kActiveWithIndex) {
2179 errorf(pos, "illegal flag value %u. Must be 0, 1, or 2", flag);
2180 return;
2181 }
2182
2183 // We know now that the flag is valid. Time to read the rest.
2184 ValueType expected_type = module_->is_memory64 ? kWasmI64 : kWasmI32;
2185 if (flag == SegmentFlags::kActiveNoIndex) {
2186 *is_active = true;
2187 *index = 0;
2188 *offset = consume_init_expr(module_.get(), expected_type);
2189 return;
2190 }
2191 if (flag == SegmentFlags::kPassive) {
2192 *is_active = false;
2193 return;
2194 }
2195 if (flag == SegmentFlags::kActiveWithIndex) {
2196 *is_active = true;
2197 *index = consume_u32v("memory index");
2198 *offset = consume_init_expr(module_.get(), expected_type);
2199 }
2200 }
2201
2202 uint32_t consume_element_func_index(ValueType expected) {
2203 WasmFunction* func = nullptr;
2204 const byte* initial_pc = pc();
2205 uint32_t index =
2206 consume_func_index(module_.get(), &func, "element function index");
2207 if (failed()) return index;
2208 DCHECK_NOT_NULL(func)((void) 0);
2209 DCHECK_EQ(index, func->func_index)((void) 0);
2210 ValueType entry_type = ValueType::Ref(func->sig_index, kNonNullable);
2211 if (V8_UNLIKELY(!IsSubtypeOf(entry_type, expected, module_.get()))(__builtin_expect(!!(!IsSubtypeOf(entry_type, expected, module_
.get())), 0))
) {
2212 errorf(initial_pc,
2213 "Invalid type in element entry: expected %s, got %s instead.",
2214 expected.name().c_str(), entry_type.name().c_str());
2215 return index;
2216 }
2217 func->declared = true;
2218 return index;
2219 }
2220};
2221
2222ModuleResult DecodeWasmModule(
2223 const WasmFeatures& enabled, const byte* module_start,
2224 const byte* module_end, bool verify_functions, ModuleOrigin origin,
2225 Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder,
2226 v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method,
2227 AccountingAllocator* allocator) {
2228 size_t size = module_end - module_start;
2229 CHECK_LE(module_start, module_end)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base
::pass_value_or_ref<decltype(module_start)>::type, typename
::v8::base::pass_value_or_ref<decltype(module_end)>::type
>((module_start), (module_end)); do { if ((__builtin_expect
(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "module_start"
" " "<=" " " "module_end"); } } while (false); } while (false
)
;
2230 size_t max_size = max_module_size();
2231 if (size > max_size) {
2232 return ModuleResult{
2233 WasmError{0, "size > maximum module size (%zu): %zu", max_size, size}};
2234 }
2235 // TODO(bradnelson): Improve histogram handling of size_t.
2236 auto size_counter =
2237 SELECT_WASM_COUNTER(counters, origin, wasm, module_size_bytes)((origin) == kWasmOrigin ? (counters)->wasm_wasm_module_size_bytes
() : (counters)->wasm_asm_module_size_bytes())
;
2238 size_counter->AddSample(static_cast<int>(size));
2239 // Signatures are stored in zone memory, which have the same lifetime
2240 // as the {module}.
2241 ModuleDecoderImpl decoder(enabled, module_start, module_end, origin);
2242 v8::metrics::WasmModuleDecoded metrics_event;
2243 base::ElapsedTimer timer;
2244 timer.Start();
2245 base::ThreadTicks thread_ticks = base::ThreadTicks::IsSupported()
2246 ? base::ThreadTicks::Now()
2247 : base::ThreadTicks();
2248 ModuleResult result =
2249 decoder.DecodeModule(counters, allocator, verify_functions);
2250
2251 // Record event metrics.
2252 metrics_event.wall_clock_duration_in_us = timer.Elapsed().InMicroseconds();
2253 timer.Stop();
2254 if (!thread_ticks.IsNull()) {
2255 metrics_event.cpu_duration_in_us =
2256 (base::ThreadTicks::Now() - thread_ticks).InMicroseconds();
2257 }
2258 metrics_event.success = decoder.ok() && result.ok();
2259 metrics_event.async = decoding_method == DecodingMethod::kAsync ||
2260 decoding_method == DecodingMethod::kAsyncStream;
2261 metrics_event.streamed = decoding_method == DecodingMethod::kSyncStream ||
2262 decoding_method == DecodingMethod::kAsyncStream;
2263 if (result.ok()) {
2264 metrics_event.function_count = result.value()->num_declared_functions;
2265 } else if (auto&& module = decoder.shared_module()) {
2266 metrics_event.function_count = module->num_declared_functions;
2267 }
2268 metrics_event.module_size_in_bytes = size;
2269 metrics_recorder->DelayMainThreadEvent(metrics_event, context_id);
2270
2271 return result;
2272}
2273
2274ModuleDecoder::ModuleDecoder(const WasmFeatures& enabled)
2275 : enabled_features_(enabled) {}
2276
2277ModuleDecoder::~ModuleDecoder() = default;
2278
2279const std::shared_ptr<WasmModule>& ModuleDecoder::shared_module() const {
2280 return impl_->shared_module();
2281}
2282
2283void ModuleDecoder::StartDecoding(
2284 Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder,
2285 v8::metrics::Recorder::ContextId context_id, AccountingAllocator* allocator,
2286 ModuleOrigin origin) {
2287 DCHECK_NULL(impl_)((void) 0);
2288 impl_.reset(new ModuleDecoderImpl(enabled_features_, origin));
2289 impl_->StartDecoding(counters, allocator);
2290}
2291
2292void ModuleDecoder::DecodeModuleHeader(base::Vector<const uint8_t> bytes,
2293 uint32_t offset) {
2294 impl_->DecodeModuleHeader(bytes, offset);
2295}
2296
2297void ModuleDecoder::DecodeSection(SectionCode section_code,
2298 base::Vector<const uint8_t> bytes,
2299 uint32_t offset, bool verify_functions) {
2300 impl_->DecodeSection(section_code, bytes, offset, verify_functions);
1
Calling 'ModuleDecoderImpl::DecodeSection'
2301}
2302
2303void ModuleDecoder::DecodeFunctionBody(uint32_t index, uint32_t length,
2304 uint32_t offset, bool verify_functions) {
2305 impl_->DecodeFunctionBody(index, length, offset, verify_functions);
2306}
2307
2308void ModuleDecoder::StartCodeSection() { impl_->StartCodeSection(); }
2309
2310bool ModuleDecoder::CheckFunctionsCount(uint32_t functions_count,
2311 uint32_t error_offset) {
2312 return impl_->CheckFunctionsCount(functions_count, error_offset);
2313}
2314
2315ModuleResult ModuleDecoder::FinishDecoding(bool verify_functions) {
2316 return impl_->FinishDecoding(verify_functions);
2317}
2318
2319void ModuleDecoder::set_code_section(uint32_t offset, uint32_t size) {
2320 return impl_->set_code_section(offset, size);
2321}
2322
2323size_t ModuleDecoder::IdentifyUnknownSection(ModuleDecoder* decoder,
2324 base::Vector<const uint8_t> bytes,
2325 uint32_t offset,
2326 SectionCode* result) {
2327 if (!decoder->ok()) return 0;
2328 decoder->impl_->Reset(bytes, offset);
2329 *result = IdentifyUnknownSectionInternal(decoder->impl_.get());
2330 return decoder->impl_->pc() - bytes.begin();
2331}
2332
2333bool ModuleDecoder::ok() { return impl_->ok(); }
2334
2335const FunctionSig* DecodeWasmSignatureForTesting(const WasmFeatures& enabled,
2336 Zone* zone, const byte* start,
2337 const byte* end) {
2338 ModuleDecoderImpl decoder(enabled, start, end, kWasmOrigin);
2339 return decoder.DecodeFunctionSignature(zone, start);
2340}
2341
2342ConstantExpression DecodeWasmInitExprForTesting(const WasmFeatures& enabled,
2343 const byte* start,
2344 const byte* end,
2345 ValueType expected) {
2346 ModuleDecoderImpl decoder(enabled, start, end, kWasmOrigin);
2347 AccountingAllocator allocator;
2348 decoder.StartDecoding(nullptr, &allocator);
2349 return decoder.DecodeInitExprForTesting(expected);
2350}
2351
2352FunctionResult DecodeWasmFunctionForTesting(
2353 const WasmFeatures& enabled, Zone* zone, const ModuleWireBytes& wire_bytes,
2354 const WasmModule* module, const byte* function_start,
2355 const byte* function_end, Counters* counters) {
2356 size_t size = function_end - function_start;
2357 CHECK_LE(function_start, function_end)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base
::pass_value_or_ref<decltype(function_start)>::type, typename
::v8::base::pass_value_or_ref<decltype(function_end)>::
type>((function_start), (function_end)); do { if ((__builtin_expect
(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "function_start"
" " "<=" " " "function_end"); } } while (false); } while (
false)
;
2358 if (size > kV8MaxWasmFunctionSize) {
2359 return FunctionResult{WasmError{0,
2360 "size > maximum function size (%zu): %zu",
2361 kV8MaxWasmFunctionSize, size}};
2362 }
2363 ModuleDecoderImpl decoder(enabled, function_start, function_end, kWasmOrigin);
2364 decoder.SetCounters(counters);
2365 return decoder.DecodeSingleFunction(zone, wire_bytes, module,
2366 std::make_unique<WasmFunction>());
2367}
2368
2369AsmJsOffsetsResult DecodeAsmJsOffsets(
2370 base::Vector<const uint8_t> encoded_offsets) {
2371 std::vector<AsmJsOffsetFunctionEntries> functions;
2372
2373 Decoder decoder(encoded_offsets);
2374 uint32_t functions_count = decoder.consume_u32v("functions count");
2375 // Consistency check.
2376 DCHECK_GE(encoded_offsets.size(), functions_count)((void) 0);
2377 functions.reserve(functions_count);
2378
2379 for (uint32_t i = 0; i < functions_count; ++i) {
2380 uint32_t size = decoder.consume_u32v("table size");
2381 if (size == 0) {
2382 functions.emplace_back();
2383 continue;
2384 }
2385 DCHECK(decoder.checkAvailable(size))((void) 0);
2386 const byte* table_end = decoder.pc() + size;
2387 uint32_t locals_size = decoder.consume_u32v("locals size");
2388 int function_start_position = decoder.consume_u32v("function start pos");
2389 int function_end_position = function_start_position;
2390 int last_byte_offset = locals_size;
2391 int last_asm_position = function_start_position;
2392 std::vector<AsmJsOffsetEntry> func_asm_offsets;
2393 func_asm_offsets.reserve(size / 4); // conservative estimation
2394 // Add an entry for the stack check, associated with position 0.
2395 func_asm_offsets.push_back(
2396 {0, function_start_position, function_start_position});
2397 while (decoder.pc() < table_end) {
2398 DCHECK(decoder.ok())((void) 0);
2399 last_byte_offset += decoder.consume_u32v("byte offset delta");
2400 int call_position =
2401 last_asm_position + decoder.consume_i32v("call position delta");
2402 int to_number_position =
2403 call_position + decoder.consume_i32v("to_number position delta");
2404 last_asm_position = to_number_position;
2405 if (decoder.pc() == table_end) {
2406 // The last entry is the function end marker.
2407 DCHECK_EQ(call_position, to_number_position)((void) 0);
2408 function_end_position = call_position;
2409 } else {
2410 func_asm_offsets.push_back(
2411 {last_byte_offset, call_position, to_number_position});
2412 }
2413 }
2414 DCHECK_EQ(decoder.pc(), table_end)((void) 0);
2415 functions.emplace_back(AsmJsOffsetFunctionEntries{
2416 function_start_position, function_end_position,
2417 std::move(func_asm_offsets)});
2418 }
2419 DCHECK(decoder.ok())((void) 0);
2420 DCHECK(!decoder.more())((void) 0);
2421
2422 return decoder.toResult(AsmJsOffsets{std::move(functions)});
2423}
2424
2425std::vector<CustomSectionOffset> DecodeCustomSections(const byte* start,
2426 const byte* end) {
2427 Decoder decoder(start, end);
2428 decoder.consume_bytes(4, "wasm magic");
2429 decoder.consume_bytes(4, "wasm version");
2430
2431 std::vector<CustomSectionOffset> result;
2432
2433 while (decoder.more()) {
2434 byte section_code = decoder.consume_u8("section code");
2435 uint32_t section_length = decoder.consume_u32v("section length");
2436 uint32_t section_start = decoder.pc_offset();
2437 if (section_code != 0) {
2438 // Skip known sections.
2439 decoder.consume_bytes(section_length, "section bytes");
2440 continue;
2441 }
2442 uint32_t name_length = decoder.consume_u32v("name length");
2443 uint32_t name_offset = decoder.pc_offset();
2444 decoder.consume_bytes(name_length, "section name");
2445 uint32_t payload_offset = decoder.pc_offset();
2446 if (section_length < (payload_offset - section_start)) {
2447 decoder.error("invalid section length");
2448 break;
2449 }
2450 uint32_t payload_length = section_length - (payload_offset - section_start);
2451 decoder.consume_bytes(payload_length);
2452 if (decoder.failed()) break;
2453 result.push_back({{section_start, section_length},
2454 {name_offset, name_length},
2455 {payload_offset, payload_length}});
2456 }
2457
2458 return result;
2459}
2460
2461namespace {
2462
2463bool FindNameSection(Decoder* decoder) {
2464 static constexpr int kModuleHeaderSize = 8;
2465 decoder->consume_bytes(kModuleHeaderSize, "module header");
2466
2467 WasmSectionIterator section_iter(decoder);
2468
2469 while (decoder->ok() && section_iter.more() &&
2470 section_iter.section_code() != kNameSectionCode) {
2471 section_iter.advance(true);
2472 }
2473 if (!section_iter.more()) return false;
2474
2475 // Reset the decoder to not read beyond the name section end.
2476 decoder->Reset(section_iter.payload(), decoder->pc_offset());
2477 return true;
2478}
2479
2480} // namespace
2481
2482void DecodeFunctionNames(const byte* module_start, const byte* module_end,
2483 std::unordered_map<uint32_t, WireBytesRef>* names) {
2484 DCHECK_NOT_NULL(names)((void) 0);
2485 DCHECK(names->empty())((void) 0);
2486
2487 Decoder decoder(module_start, module_end);
2488 if (FindNameSection(&decoder)) {
2489 while (decoder.ok() && decoder.more()) {
2490 uint8_t name_type = decoder.consume_u8("name type");
2491 if (name_type & 0x80) break; // no varuint7
2492
2493 uint32_t name_payload_len = decoder.consume_u32v("name payload length");
2494 if (!decoder.checkAvailable(name_payload_len)) break;
2495
2496 if (name_type != NameSectionKindCode::kFunctionCode) {
2497 decoder.consume_bytes(name_payload_len, "name subsection payload");
2498 continue;
2499 }
2500 uint32_t functions_count = decoder.consume_u32v("functions count");
2501
2502 for (; decoder.ok() && functions_count > 0; --functions_count) {
2503 uint32_t function_index = decoder.consume_u32v("function index");
2504 WireBytesRef name = consume_string(&decoder, false, "function name");
2505
2506 // Be lenient with errors in the name section: Ignore non-UTF8 names.
2507 // You can even assign to the same function multiple times (last valid
2508 // one wins).
2509 if (decoder.ok() && validate_utf8(&decoder, name)) {
2510 names->insert(std::make_pair(function_index, name));
2511 }
2512 }
2513 }
2514 }
2515}
2516
2517NameMap DecodeNameMap(base::Vector<const uint8_t> module_bytes,
2518 uint8_t name_section_kind) {
2519 Decoder decoder(module_bytes);
2520 if (!FindNameSection(&decoder)) return NameMap{{}};
2521
2522 std::vector<NameAssoc> names;
2523 while (decoder.ok() && decoder.more()) {
2524 uint8_t name_type = decoder.consume_u8("name type");
2525 if (name_type & 0x80) break; // no varuint7
2526
2527 uint32_t name_payload_len = decoder.consume_u32v("name payload length");
2528 if (!decoder.checkAvailable(name_payload_len)) break;
2529
2530 if (name_type != name_section_kind) {
2531 decoder.consume_bytes(name_payload_len, "name subsection payload");
2532 continue;
2533 }
2534
2535 uint32_t count = decoder.consume_u32v("names count");
2536 for (uint32_t i = 0; i < count; i++) {
2537 uint32_t index = decoder.consume_u32v("index");
2538 WireBytesRef name = consume_string(&decoder, false, "name");
2539 if (!decoder.ok()) break;
2540 if (index > kMaxInt) continue;
2541 if (!validate_utf8(&decoder, name)) continue;
2542 names.emplace_back(static_cast<int>(index), name);
2543 }
2544 }
2545 std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{});
2546 return NameMap{std::move(names)};
2547}
2548
2549IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes,
2550 uint8_t name_section_kind) {
2551 Decoder decoder(module_bytes);
2552 if (!FindNameSection(&decoder)) return IndirectNameMap{{}};
2553
2554 std::vector<IndirectNameMapEntry> entries;
2555 while (decoder.ok() && decoder.more()) {
2556 uint8_t name_type = decoder.consume_u8("name type");
2557 if (name_type & 0x80) break; // no varuint7
2558
2559 uint32_t name_payload_len = decoder.consume_u32v("name payload length");
2560 if (!decoder.checkAvailable(name_payload_len)) break;
2561
2562 if (name_type != name_section_kind) {
2563 decoder.consume_bytes(name_payload_len, "name subsection payload");
2564 continue;
2565 }
2566
2567 uint32_t outer_count = decoder.consume_u32v("outer count");
2568 for (uint32_t i = 0; i < outer_count; ++i) {
2569 uint32_t outer_index = decoder.consume_u32v("outer index");
2570 if (outer_index > kMaxInt) continue;
2571 std::vector<NameAssoc> names;
2572 uint32_t inner_count = decoder.consume_u32v("inner count");
2573 for (uint32_t k = 0; k < inner_count; ++k) {
2574 uint32_t inner_index = decoder.consume_u32v("inner index");
2575 WireBytesRef name = consume_string(&decoder, false, "name");
2576 if (!decoder.ok()) break;
2577 if (inner_index > kMaxInt) continue;
2578 // Ignore non-utf8 names.
2579 if (!validate_utf8(&decoder, name)) continue;
2580 names.emplace_back(static_cast<int>(inner_index), name);
2581 }
2582 // Use stable sort to get deterministic names (the first one declared)
2583 // even in the presence of duplicates.
2584 std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{});
2585 entries.emplace_back(static_cast<int>(outer_index), std::move(names));
2586 }
2587 }
2588 std::stable_sort(entries.begin(), entries.end(),
2589 IndirectNameMapEntry::IndexLess{});
2590 return IndirectNameMap{std::move(entries)};
2591}
2592
2593#undef TRACE
2594
2595} // namespace wasm
2596} // namespace internal
2597} // namespace v8

../deps/v8/src/wasm/wasm-module.h

1// Copyright 2015 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#if !V8_ENABLE_WEBASSEMBLY1
6#error This header should only be included if WebAssembly is enabled.
7#endif // !V8_ENABLE_WEBASSEMBLY
8
9#ifndef V8_WASM_WASM_MODULE_H_
10#define V8_WASM_WASM_MODULE_H_
11
12#include <map>
13#include <memory>
14
15#include "src/base/optional.h"
16#include "src/base/platform/wrappers.h"
17#include "src/base/vector.h"
18#include "src/common/globals.h"
19#include "src/handles/handles.h"
20#include "src/wasm/branch-hint-map.h"
21#include "src/wasm/signature-map.h"
22#include "src/wasm/struct-types.h"
23#include "src/wasm/wasm-constants.h"
24#include "src/wasm/wasm-init-expr.h"
25#include "src/wasm/wasm-limits.h"
26
27namespace v8 {
28
29namespace internal {
30
31class WasmModuleObject;
32
33namespace wasm {
34
35using WasmName = base::Vector<const char>;
36
37struct AsmJsOffsets;
38class ErrorThrower;
39
40// Reference to a string in the wire bytes.
41class WireBytesRef {
42 public:
43 WireBytesRef() : WireBytesRef(0, 0) {}
44 WireBytesRef(uint32_t offset, uint32_t length)
45 : offset_(offset), length_(length) {
46 DCHECK_IMPLIES(offset_ == 0, length_ == 0)((void) 0);
47 DCHECK_LE(offset_, offset_ + length_)((void) 0); // no uint32_t overflow.
48 }
49
50 uint32_t offset() const { return offset_; }
51 uint32_t length() const { return length_; }
52 uint32_t end_offset() const { return offset_ + length_; }
53 bool is_empty() const { return length_ == 0; }
54 bool is_set() const { return offset_ != 0; }
55
56 private:
57 uint32_t offset_;
58 uint32_t length_;
59};
60
61// Static representation of a wasm function.
62struct WasmFunction {
63 const FunctionSig* sig; // signature of the function.
64 uint32_t func_index; // index into the function table.
65 uint32_t sig_index; // index into the signature table.
66 WireBytesRef code; // code of this function.
67 // Required number of slots in a feedback vector. Marked {mutable} because
68 // this is computed late (by Liftoff compilation), when the rest of the
69 // {WasmFunction} is typically considered {const}.
70 mutable int feedback_slots;
71 bool imported;
72 bool exported;
73 bool declared;
74};
75
76// A representation of a constant expression. The most common expression types
77// are hard-coded, while the rest are represented as a {WireBytesRef}.
78class ConstantExpression {
79 public:
80 enum Kind {
81 kEmpty,
82 kI32Const,
83 kRefNull,
84 kRefFunc,
85 kWireBytesRef,
86 kLastKind = kWireBytesRef
87 };
88
89 union Value {
90 int32_t i32_value;
91 uint32_t index_or_offset;
92 HeapType::Representation repr;
93 };
94
95 ConstantExpression() : bit_field_(KindField::encode(kEmpty)) {}
96
97 static ConstantExpression I32Const(int32_t value) {
98 return ConstantExpression(ValueField::encode(value) |
99 KindField::encode(kI32Const));
100 }
101 static ConstantExpression RefFunc(uint32_t index) {
102 return ConstantExpression(ValueField::encode(index) |
103 KindField::encode(kRefFunc));
104 }
105 static ConstantExpression RefNull(HeapType::Representation repr) {
106 return ConstantExpression(ValueField::encode(repr) |
107 KindField::encode(kRefNull));
108 }
109 static ConstantExpression WireBytes(uint32_t offset, uint32_t length) {
110 return ConstantExpression(OffsetField::encode(offset) |
111 LengthField::encode(length) |
112 KindField::encode(kWireBytesRef));
113 }
114
115 Kind kind() const { return KindField::decode(bit_field_); }
116
117 bool is_set() const { return kind() != kEmpty; }
118
119 uint32_t index() const {
120 DCHECK_EQ(kind(), kRefFunc)((void) 0);
121 return ValueField::decode(bit_field_);
122 }
123
124 HeapType::Representation repr() const {
125 DCHECK_EQ(kind(), kRefNull)((void) 0);
126 return static_cast<HeapType::Representation>(
127 ValueField::decode(bit_field_));
128 }
129
130 int32_t i32_value() const {
131 DCHECK_EQ(kind(), kI32Const)((void) 0);
132 return ValueField::decode(bit_field_);
133 }
134
135 WireBytesRef wire_bytes_ref() const {
136 DCHECK_EQ(kind(), kWireBytesRef)((void) 0);
137 return WireBytesRef(OffsetField::decode(bit_field_),
138 LengthField::decode(bit_field_));
139 }
140
141 private:
142 static constexpr int kValueBits = 32;
143 static constexpr int kLengthBits = 30;
144 static constexpr int kOffsetBits = 30;
145 static constexpr int kKindBits = 3;
146
147 // There are two possible combinations of fields: offset + length + kind if
148 // kind = kWireBytesRef, or value + kind for anything else.
149 using ValueField = base::BitField<uint32_t, 0, kValueBits, uint64_t>;
150 using OffsetField = base::BitField<uint32_t, 0, kOffsetBits, uint64_t>;
151 using LengthField = OffsetField::Next<uint32_t, kLengthBits>;
152 using KindField = LengthField::Next<Kind, kKindBits>;
153
154 // Make sure we reserve enough bits for a {WireBytesRef}'s length and offset.
155 STATIC_ASSERT(kV8MaxWasmModuleSize <= LengthField::kMax + 1)static_assert(kV8MaxWasmModuleSize <= LengthField::kMax + 1
, "kV8MaxWasmModuleSize <= LengthField::kMax + 1")
;
156 STATIC_ASSERT(kV8MaxWasmModuleSize <= OffsetField::kMax + 1)static_assert(kV8MaxWasmModuleSize <= OffsetField::kMax + 1
, "kV8MaxWasmModuleSize <= OffsetField::kMax + 1")
;
157 // Make sure kind fits in kKindBits.
158 STATIC_ASSERT(kLastKind <= KindField::kMax + 1)static_assert(kLastKind <= KindField::kMax + 1, "kLastKind <= KindField::kMax + 1"
)
;
159
160 explicit ConstantExpression(uint64_t bit_field) : bit_field_(bit_field) {}
161
162 uint64_t bit_field_;
163};
164
165// We want to keep {ConstantExpression} small to reduce memory usage during
166// compilation/instantiation.
167STATIC_ASSERT(sizeof(ConstantExpression) <= 8)static_assert(sizeof(ConstantExpression) <= 8, "sizeof(ConstantExpression) <= 8"
)
;
168
169// Static representation of a wasm global variable.
170struct WasmGlobal {
171 ValueType type; // type of the global.
172 bool mutability; // {true} if mutable.
173 ConstantExpression init; // the initialization expression of the global.
174 union {
175 // Index of imported mutable global.
176 uint32_t index;
177 // Offset into global memory (if not imported & mutable). Expressed in bytes
178 // for value-typed globals, and in tagged words for reference-typed globals.
179 uint32_t offset;
180 };
181 bool imported; // true if imported.
182 bool exported; // true if exported.
183};
184
185// Note: An exception tag signature only uses the params portion of a function
186// signature.
187using WasmTagSig = FunctionSig;
188
189// Static representation of a wasm tag type.
190struct WasmTag {
191 explicit WasmTag(const WasmTagSig* sig) : sig(sig) {}
192 const FunctionSig* ToFunctionSig() const { return sig; }
193
194 const WasmTagSig* sig; // type signature of the tag.
195};
196
197// Static representation of a wasm data segment.
198struct WasmDataSegment {
199 // Construct an active segment.
200 explicit WasmDataSegment(ConstantExpression dest_addr)
201 : dest_addr(dest_addr), active(true) {}
202
203 // Construct a passive segment, which has no dest_addr.
204 WasmDataSegment() : active(false) {}
205
206 ConstantExpression dest_addr; // destination memory address of the data.
207 WireBytesRef source; // start offset in the module bytes.
208 bool active = true; // true if copied automatically during instantiation.
209};
210
211// Static representation of wasm element segment (table initializer).
212struct WasmElemSegment {
213 enum Status {
214 kStatusActive, // copied automatically during instantiation.
215 kStatusPassive, // copied explicitly after instantiation.
216 kStatusDeclarative // purely declarative and never copied.
217 };
218 enum ElementType { kFunctionIndexElements, kExpressionElements };
219
220 // Construct an active segment.
221 WasmElemSegment(ValueType type, uint32_t table_index,
222 ConstantExpression offset, ElementType element_type)
223 : status(kStatusActive),
224 type(type),
225 table_index(table_index),
226 offset(std::move(offset)),
227 element_type(element_type) {}
228
229 // Construct a passive or declarative segment, which has no table index or
230 // offset.
231 WasmElemSegment(ValueType type, Status status, ElementType element_type)
232 : status(status), type(type), table_index(0), element_type(element_type) {
233 DCHECK_NE(status, kStatusActive)((void) 0);
234 }
235
236 // Default constructor. Constucts an invalid segment.
237 WasmElemSegment()
238 : status(kStatusActive),
239 type(kWasmBottom),
240 table_index(0),
241 element_type(kFunctionIndexElements) {}
242
243 WasmElemSegment(const WasmElemSegment&) = delete;
244 WasmElemSegment(WasmElemSegment&&) V8_NOEXCEPTnoexcept = default;
245 WasmElemSegment& operator=(const WasmElemSegment&) = delete;
246 WasmElemSegment& operator=(WasmElemSegment&&) V8_NOEXCEPTnoexcept = default;
247
248 Status status;
249 ValueType type;
250 uint32_t table_index;
251 ConstantExpression offset;
252 ElementType element_type;
253 std::vector<ConstantExpression> entries;
254};
255
256// Static representation of a wasm import.
257struct WasmImport {
258 WireBytesRef module_name; // module name.
259 WireBytesRef field_name; // import name.
260 ImportExportKindCode kind; // kind of the import.
261 uint32_t index; // index into the respective space.
262};
263
264// Static representation of a wasm export.
265struct WasmExport {
266 WireBytesRef name; // exported name.
267 ImportExportKindCode kind; // kind of the export.
268 uint32_t index; // index into the respective space.
269};
270
271enum class WasmCompilationHintStrategy : uint8_t {
272 kDefault = 0,
273 kLazy = 1,
274 kEager = 2,
275 kLazyBaselineEagerTopTier = 3,
276};
277
278enum class WasmCompilationHintTier : uint8_t {
279 kDefault = 0,
280 kBaseline = 1,
281 kOptimized = 2,
282};
283
284// Static representation of a wasm compilation hint
285struct WasmCompilationHint {
286 WasmCompilationHintStrategy strategy;
287 WasmCompilationHintTier baseline_tier;
288 WasmCompilationHintTier top_tier;
289};
290
291enum ModuleOrigin : uint8_t {
292 kWasmOrigin,
293 kAsmJsSloppyOrigin,
294 kAsmJsStrictOrigin
295};
296
297#define SELECT_WASM_COUNTER(counters, origin, prefix, suffix)((origin) == kWasmOrigin ? (counters)->prefix_wasm_suffix(
) : (counters)->prefix_asm_suffix())
\
298 ((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \
299 : (counters)->prefix##_asm_##suffix())
300
301struct ModuleWireBytes;
302
303class V8_EXPORT_PRIVATE LazilyGeneratedNames {
304 public:
305 WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes,
306 uint32_t function_index) const;
307
308 void AddForTesting(int function_index, WireBytesRef name);
309
310 private:
311 // {function_names_} are populated lazily after decoding, and
312 // therefore need a mutex to protect concurrent modifications
313 // from multiple {WasmModuleObject}.
314 mutable base::Mutex mutex_;
315 mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>>
316 function_names_;
317};
318
319class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
320 public:
321 explicit AsmJsOffsetInformation(base::Vector<const byte> encoded_offsets);
322
323 // Destructor defined in wasm-module.cc, where the definition of
324 // {AsmJsOffsets} is available.
325 ~AsmJsOffsetInformation();
326
327 int GetSourcePosition(int func_index, int byte_offset,
328 bool is_at_number_conversion);
329
330 std::pair<int, int> GetFunctionOffsets(int func_index);
331
332 private:
333 void EnsureDecodedOffsets();
334
335 // The offset information table is decoded lazily, hence needs to be
336 // protected against concurrent accesses.
337 // Exactly one of the two fields below will be set at a time.
338 mutable base::Mutex mutex_;
339
340 // Holds the encoded offset table bytes.
341 base::OwnedVector<const uint8_t> encoded_offsets_;
342
343 // Holds the decoded offset table.
344 std::unique_ptr<AsmJsOffsets> decoded_offsets_;
345};
346
347// Used as the supertype for a type at the top of the type hierarchy.
348constexpr uint32_t kNoSuperType = std::numeric_limits<uint32_t>::max();
349
350struct TypeDefinition {
351 enum Kind { kFunction, kStruct, kArray };
352
353 TypeDefinition(const FunctionSig* sig, uint32_t supertype)
354 : function_sig(sig), supertype(supertype), kind(kFunction) {}
355 TypeDefinition(const StructType* type, uint32_t supertype)
356 : struct_type(type), supertype(supertype), kind(kStruct) {}
357 TypeDefinition(const ArrayType* type, uint32_t supertype)
358 : array_type(type), supertype(supertype), kind(kArray) {}
359 TypeDefinition()
360 : function_sig(nullptr), supertype(kNoSuperType), kind(kFunction) {}
361
362 union {
363 const FunctionSig* function_sig;
364 const StructType* struct_type;
365 const ArrayType* array_type;
366 };
367
368 bool operator==(const TypeDefinition& other) const {
369 if (supertype != other.supertype || kind != other.kind) {
370 return false;
371 }
372 switch (kind) {
373 case kFunction:
374 return *function_sig == *other.function_sig;
375 case kStruct:
376 return *struct_type == *other.struct_type;
377 case kArray:
378 return *array_type == *other.array_type;
379 }
380 }
381
382 bool operator!=(const TypeDefinition& other) const {
383 return !(*this == other);
384 }
385
386 uint32_t supertype;
387 Kind kind;
388};
389
390struct V8_EXPORT_PRIVATE WasmDebugSymbols {
391 enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF };
392 Type type = Type::None;
393 WireBytesRef external_url;
394};
395
396struct CallSiteFeedback {
397 int function_index;
398 int absolute_call_frequency;
399};
400struct FunctionTypeFeedback {
401 std::vector<CallSiteFeedback> feedback_vector;
402 std::map<WasmCodePosition, int> positions;
403 int tierup_priority = 0;
404};
405struct TypeFeedbackStorage {
406 std::map<uint32_t, FunctionTypeFeedback> feedback_for_function;
407 // Accesses to {feedback_for_function} are guarded by this mutex.
408 base::Mutex mutex;
409};
410
411struct WasmTable;
412
413// Static representation of a module.
414struct V8_EXPORT_PRIVATE WasmModule {
415 std::unique_ptr<Zone> signature_zone;
416 uint32_t initial_pages = 0; // initial size of the memory in 64k pages
417 uint32_t maximum_pages = 0; // maximum size of the memory in 64k pages
418 bool has_shared_memory = false; // true if memory is a SharedArrayBuffer
419 bool has_maximum_pages = false; // true if there is a maximum memory size
420 bool is_memory64 = false; // true if the memory is 64 bit
421 bool has_memory = false; // true if the memory was defined or imported
422 bool mem_export = false; // true if the memory is exported
423 int start_function_index = -1; // start function, >= 0 if any
424
425 // Size of the buffer required for all globals that are not imported and
426 // mutable.
427 uint32_t untagged_globals_buffer_size = 0;
428 uint32_t tagged_globals_buffer_size = 0;
429 uint32_t num_imported_mutable_globals = 0;
430 uint32_t num_imported_functions = 0;
431 uint32_t num_imported_tables = 0;
432 uint32_t num_declared_functions = 0; // excluding imported
433 uint32_t num_exported_functions = 0;
434 uint32_t num_declared_data_segments = 0; // From the DataCount section.
435 // Position and size of the code section (payload only, i.e. without section
436 // ID and length).
437 WireBytesRef code = {0, 0};
438 WireBytesRef name = {0, 0};
439
440 void add_type(TypeDefinition type) {
441 types.push_back(type);
442 uint32_t canonical_id = type.kind
23.1
Field 'kind' is equal to kFunction
23.1
Field 'kind' is equal to kFunction
== TypeDefinition::kFunction
24
'?' condition is true
443 ? signature_map.FindOrInsert(*type.function_sig)
25
Forming reference to null pointer
444 : 0;
445 canonicalized_type_ids.push_back(canonical_id);
446 // Canonical type will be computed later.
447 isorecursive_canonical_type_ids.push_back(kNoSuperType);
448 }
449
450 bool has_type(uint32_t index) const { return index < types.size(); }
451
452 void add_signature(const FunctionSig* sig, uint32_t supertype) {
453 DCHECK_NOT_NULL(sig)((void) 0);
454 add_type(TypeDefinition(sig, supertype));
455 }
456 bool has_signature(uint32_t index) const {
457 return index < types.size() &&
458 types[index].kind == TypeDefinition::kFunction;
459 }
460 const FunctionSig* signature(uint32_t index) const {
461 DCHECK(has_signature(index))((void) 0);
462 return types[index].function_sig;
463 }
464
465 void add_struct_type(const StructType* type, uint32_t supertype) {
466 DCHECK_NOT_NULL(type)((void) 0);
467 add_type(TypeDefinition(type, supertype));
468 }
469 bool has_struct(uint32_t index) const {
470 return index < types.size() && types[index].kind == TypeDefinition::kStruct;
471 }
472 const StructType* struct_type(uint32_t index) const {
473 DCHECK(has_struct(index))((void) 0);
474 return types[index].struct_type;
475 }
476
477 void add_array_type(const ArrayType* type, uint32_t supertype) {
478 DCHECK_NOT_NULL(type)((void) 0);
479 add_type(TypeDefinition(type, supertype));
480 }
481 bool has_array(uint32_t index) const {
482 return index < types.size() && types[index].kind == TypeDefinition::kArray;
483 }
484 const ArrayType* array_type(uint32_t index) const {
485 DCHECK(has_array(index))((void) 0);
486 return types[index].array_type;
487 }
488
489 uint32_t supertype(uint32_t index) const {
490 DCHECK(index < types.size())((void) 0);
491 return types[index].supertype;
492 }
493 bool has_supertype(uint32_t index) const {
494 return supertype(index) != kNoSuperType;
495 }
496
497 std::vector<TypeDefinition> types; // by type index
498 // TODO(7748): Unify the following two arrays.
499 // Maps each type index to a canonical index for purposes of call_indirect.
500 std::vector<uint32_t> canonicalized_type_ids;
501 // Maps each type index to its global (cross-module) canonical index as per
502 // isorecursive type canonicalization.
503 std::vector<uint32_t> isorecursive_canonical_type_ids;
504 // Canonicalizing map for signature indexes.
505 SignatureMap signature_map;
506 std::vector<WasmFunction> functions;
507 std::vector<WasmGlobal> globals;
508 std::vector<WasmDataSegment> data_segments;
509 std::vector<WasmTable> tables;
510 std::vector<WasmImport> import_table;
511 std::vector<WasmExport> export_table;
512 std::vector<WasmTag> tags;
513 std::vector<WasmElemSegment> elem_segments;
514 std::vector<WasmCompilationHint> compilation_hints;
515 BranchHintInfo branch_hints;
516 mutable TypeFeedbackStorage type_feedback;
517
518 ModuleOrigin origin = kWasmOrigin; // origin of the module
519 LazilyGeneratedNames lazily_generated_names;
520 WasmDebugSymbols debug_symbols;
521
522 // Asm.js source position information. Only available for modules compiled
523 // from asm.js.
524 std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information;
525
526 explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr);
527 WasmModule(const WasmModule&) = delete;
528 WasmModule& operator=(const WasmModule&) = delete;
529};
530
531// Static representation of a wasm indirect call table.
532struct WasmTable {
533 MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable)WasmTable() = default; WasmTable(WasmTable&&) noexcept
= default; WasmTable& operator=(WasmTable&&) noexcept
= default; WasmTable(const WasmTable&) = delete; WasmTable
& operator=(const WasmTable&) = delete
;
534
535 // 'module' can be nullptr
536 // TODO(9495): Update this function as more table types are supported, or
537 // remove it completely when all reference types are allowed.
538 static bool IsValidTableType(ValueType type, const WasmModule* module) {
539 if (!type.is_object_reference()) return false;
540 HeapType heap_type = type.heap_type();
541 return heap_type == HeapType::kFunc || heap_type == HeapType::kAny ||
542 (module != nullptr && heap_type.is_index() &&
543 module->has_signature(heap_type.ref_index()));
544 }
545
546 ValueType type = kWasmVoid; // table type.
547 uint32_t initial_size = 0; // initial table size.
548 uint32_t maximum_size = 0; // maximum table size.
549 bool has_maximum_size = false; // true if there is a maximum size.
550 bool imported = false; // true if imported.
551 bool exported = false; // true if exported.
552 ConstantExpression initial_value;
553};
554
555inline bool is_asmjs_module(const WasmModule* module) {
556 return module->origin != kWasmOrigin;
557}
558
559size_t EstimateStoredSize(const WasmModule* module);
560
561// Returns the number of possible export wrappers for a given module.
562V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module);
563
564// Returns the wrapper index for a function in {module} with signature {sig}
565// or {sig_index} and origin defined by {is_import}.
566// Prefer to use the {sig_index} consuming version, as it is much faster.
567int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
568 bool is_import);
569int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index,
570 bool is_import);
571
572// Return the byte offset of the function identified by the given index.
573// The offset will be relative to the start of the module bytes.
574// Returns -1 if the function index is invalid.
575int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index);
576
577// Returns the function containing the given byte offset.
578// Returns -1 if the byte offset is not contained in any
579// function of this module.
580int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
581
582// Returns the function containing the given byte offset.
583// Will return preceding function if the byte offset is not
584// contained within a function.
585int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);
586
587// Gets the explicitly defined subtyping depth for the given type.
588// Returns 0 if the type has no explicit supertype.
589// The result is capped to {kV8MaxRttSubtypingDepth + 1}.
590// Invalid cyclic hierarchies will return -1.
591V8_EXPORT_PRIVATE int GetSubtypingDepth(const WasmModule* module,
592 uint32_t type_index);
593
594// Interface to the storage (wire bytes) of a wasm module.
595// It is illegal for anyone receiving a ModuleWireBytes to store pointers based
596// on module_bytes, as this storage is only guaranteed to be alive as long as
597// this struct is alive.
598struct V8_EXPORT_PRIVATE ModuleWireBytes {
599 explicit ModuleWireBytes(base::Vector<const byte> module_bytes)
600 : module_bytes_(module_bytes) {}
601 ModuleWireBytes(const byte* start, const byte* end)
602 : module_bytes_(start, static_cast<int>(end - start)) {
603 DCHECK_GE(kMaxInt, end - start)((void) 0);
604 }
605
606 // Get a string stored in the module bytes representing a name.
607 WasmName GetNameOrNull(WireBytesRef ref) const;
608
609 // Get a string stored in the module bytes representing a function name.
610 WasmName GetNameOrNull(const WasmFunction* function,
611 const WasmModule* module) const;
612
613 // Checks the given reference is contained within the module bytes.
614 bool BoundsCheck(WireBytesRef ref) const {
615 uint32_t size = static_cast<uint32_t>(module_bytes_.length());
616 return ref.offset() <= size && ref.length() <= size - ref.offset();
617 }
618
619 base::Vector<const byte> GetFunctionBytes(
620 const WasmFunction* function) const {
621 return module_bytes_.SubVector(function->code.offset(),
622 function->code.end_offset());
623 }
624
625 base::Vector<const byte> module_bytes() const { return module_bytes_; }
626 const byte* start() const { return module_bytes_.begin(); }
627 const byte* end() const { return module_bytes_.end(); }
628 size_t length() const { return module_bytes_.length(); }
629
630 private:
631 base::Vector<const byte> module_bytes_;
632};
633
634// A helper for printing out the names of functions.
635struct WasmFunctionName {
636 WasmFunctionName(const WasmFunction* function, WasmName name)
637 : function_(function), name_(name) {}
638
639 const WasmFunction* function_;
640 const WasmName name_;
641};
642
643std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
644
645V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
646 Handle<Context> context);
647
648Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig,
649 bool for_exception = false);
650Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
651 ValueType type);
652Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size,
653 base::Optional<uint32_t> max_size,
654 bool shared);
655Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type,
656 uint32_t min_size,
657 base::Optional<uint32_t> max_size);
658Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module);
659Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module);
660Handle<JSArray> GetCustomSections(Isolate* isolate,
661 Handle<WasmModuleObject> module,
662 Handle<String> name, ErrorThrower* thrower);
663
664// Get the source position from a given function index and byte offset,
665// for either asm.js or pure Wasm modules.
666int GetSourcePosition(const WasmModule*, uint32_t func_index,
667 uint32_t byte_offset, bool is_at_number_conversion);
668
669// Translate function index to the index relative to the first declared (i.e.
670// non-imported) function.
671inline int declared_function_index(const WasmModule* module, int func_index) {
672 DCHECK_LE(module->num_imported_functions, func_index)((void) 0);
673 int declared_idx = func_index - module->num_imported_functions;
674 DCHECK_GT(module->num_declared_functions, declared_idx)((void) 0);
675 return declared_idx;
676}
677
678// TruncatedUserString makes it easy to output names up to a certain length, and
679// output a truncation followed by '...' if they exceed a limit.
680// Use like this:
681// TruncatedUserString<> name (pc, len);
682// printf("... %.*s ...", name.length(), name.start())
683template <int kMaxLen = 50>
684class TruncatedUserString {
685 static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)");
686
687 public:
688 template <typename T>
689 explicit TruncatedUserString(base::Vector<T> name)
690 : TruncatedUserString(name.begin(), name.length()) {}
691
692 TruncatedUserString(const byte* start, size_t len)
693 : TruncatedUserString(reinterpret_cast<const char*>(start), len) {}
694
695 TruncatedUserString(const char* start, size_t len)
696 : start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) {
697 if (len > static_cast<size_t>(kMaxLen)) {
698 memcpy(buffer_, start, kMaxLen - 3);
699 memset(buffer_ + kMaxLen - 3, '.', 3);
700 start_ = buffer_;
701 }
702 }
703
704 const char* start() const { return start_; }
705
706 int length() const { return length_; }
707
708 private:
709 const char* start_;
710 const int length_;
711 char buffer_[kMaxLen];
712};
713
714// Print the signature into the given {buffer}, using {delimiter} as separator
715// between parameter types and return types. If {buffer} is non-empty, it will
716// be null-terminated, even if the signature is cut off. Returns the number of
717// characters written, excluding the terminating null-byte.
718size_t PrintSignature(base::Vector<char> buffer, const wasm::FunctionSig*,
719 char delimiter = ':');
720
721} // namespace wasm
722} // namespace internal
723} // namespace v8
724
725#endif // V8_WASM_WASM_MODULE_H_