| File: | out/../deps/v8/src/wasm/module-decoder.cc |
| Warning: | line 2210, column 43 Access to field 'sig_index' 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 |