File: | out/../deps/v8/src/wasm/module-decoder.cc |
Warning: | line 992, column 26 Access to field 'exported' results in a dereference of a null pointer (loaded from variable 'func') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
26 | namespace v8 { | |||
27 | namespace internal { | |||
28 | namespace wasm { | |||
29 | ||||
30 | #define TRACE(...) \ | |||
31 | do { \ | |||
32 | if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \ | |||
33 | } while (false) | |||
34 | ||||
35 | namespace { | |||
36 | ||||
37 | constexpr char kNameString[] = "name"; | |||
38 | constexpr char kSourceMappingURLString[] = "sourceMappingURL"; | |||
39 | constexpr char kCompilationHintsString[] = "compilationHints"; | |||
40 | constexpr char kBranchHintsString[] = "metadata.code.branch_hint"; | |||
41 | constexpr char kDebugInfoString[] = ".debug_info"; | |||
42 | constexpr char kExternalDebugInfoString[] = "external_debug_info"; | |||
43 | ||||
44 | const 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 | ||||
62 | const 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 | ||||
109 | namespace { | |||
110 | ||||
111 | bool 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. | |||
119 | WireBytesRef 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 | ||||
135 | namespace { | |||
136 | SectionCode 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. | |||
172 | class 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. | |||
282 | class 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; | |||
397 | Reset(bytes, offset); | |||
398 | TRACE("Section: %s\n", SectionName(section_code)); | |||
399 | TRACE("Decode Section %p - %p\n", bytes.begin(), bytes.end()); | |||
400 | ||||
401 | // Check if the section is out-of-order. | |||
402 | if (section_code < next_ordered_section_ && | |||
403 | section_code < kFirstUnorderedSection) { | |||
404 | errorf(pc(), "unexpected section <%s>", SectionName(section_code)); | |||
405 | return; | |||
406 | } | |||
407 | ||||
408 | switch (section_code) { | |||
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) { | |||
463 | case kUnknownSectionCode: | |||
464 | break; | |||
465 | case kTypeSectionCode: | |||
466 | 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()) { | |||
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) { | |||
694 | uint8_t first_type_opcode = this->read_u8<Decoder::kFullValidation>(pc()); | |||
695 | if (first_type_opcode == 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 < types_count; ++i) { | |||
704 | TRACE("DecodeType[%d] module+%d\n", i, | |||
705 | static_cast<int>(pc_ - start_)); | |||
706 | TypeDefinition type = consume_nominal_type_definition(); | |||
707 | if (ok()) module_->add_type(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 | ||||
2222 | ModuleResult 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 | ||||
2274 | ModuleDecoder::ModuleDecoder(const WasmFeatures& enabled) | |||
2275 | : enabled_features_(enabled) {} | |||
2276 | ||||
2277 | ModuleDecoder::~ModuleDecoder() = default; | |||
2278 | ||||
2279 | const std::shared_ptr<WasmModule>& ModuleDecoder::shared_module() const { | |||
2280 | return impl_->shared_module(); | |||
2281 | } | |||
2282 | ||||
2283 | void 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 | ||||
2292 | void ModuleDecoder::DecodeModuleHeader(base::Vector<const uint8_t> bytes, | |||
2293 | uint32_t offset) { | |||
2294 | impl_->DecodeModuleHeader(bytes, offset); | |||
2295 | } | |||
2296 | ||||
2297 | void 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); | |||
| ||||
2301 | } | |||
2302 | ||||
2303 | void 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 | ||||
2308 | void ModuleDecoder::StartCodeSection() { impl_->StartCodeSection(); } | |||
2309 | ||||
2310 | bool ModuleDecoder::CheckFunctionsCount(uint32_t functions_count, | |||
2311 | uint32_t error_offset) { | |||
2312 | return impl_->CheckFunctionsCount(functions_count, error_offset); | |||
2313 | } | |||
2314 | ||||
2315 | ModuleResult ModuleDecoder::FinishDecoding(bool verify_functions) { | |||
2316 | return impl_->FinishDecoding(verify_functions); | |||
2317 | } | |||
2318 | ||||
2319 | void ModuleDecoder::set_code_section(uint32_t offset, uint32_t size) { | |||
2320 | return impl_->set_code_section(offset, size); | |||
2321 | } | |||
2322 | ||||
2323 | size_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 | ||||
2333 | bool ModuleDecoder::ok() { return impl_->ok(); } | |||
2334 | ||||
2335 | const 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 | ||||
2342 | ConstantExpression 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 | ||||
2352 | FunctionResult 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 | ||||
2369 | AsmJsOffsetsResult 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 | ||||
2425 | std::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 | ||||
2461 | namespace { | |||
2462 | ||||
2463 | bool 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 | ||||
2482 | void 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 | ||||
2517 | NameMap 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 | ||||
2549 | IndirectNameMap 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 |