diff options
Diffstat (limited to 'src/wasm/module-decoder.cc')
-rw-r--r-- | src/wasm/module-decoder.cc | 454 |
1 files changed, 281 insertions, 173 deletions
diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc index c8eace3c..440e5dcb 100644 --- a/src/wasm/module-decoder.cc +++ b/src/wasm/module-decoder.cc @@ -3,15 +3,19 @@ // found in the LICENSE file. #include "src/wasm/module-decoder.h" +#include "src/wasm/function-body-decoder-impl.h" #include "src/base/functional.h" #include "src/base/platform/platform.h" +#include "src/counters.h" #include "src/flags.h" #include "src/macro-assembler.h" -#include "src/objects.h" +#include "src/objects-inl.h" +#include "src/ostreams.h" #include "src/v8.h" #include "src/wasm/decoder.h" +#include "src/wasm/wasm-limits.h" namespace v8 { namespace internal { @@ -26,30 +30,63 @@ namespace wasm { #define TRACE(...) #endif +const char* SectionName(WasmSectionCode code) { + switch (code) { + case kUnknownSectionCode: + return "Unknown"; + case kTypeSectionCode: + return "Type"; + case kImportSectionCode: + return "Import"; + case kFunctionSectionCode: + return "Function"; + case kTableSectionCode: + return "Table"; + case kMemorySectionCode: + return "Memory"; + case kGlobalSectionCode: + return "Global"; + case kExportSectionCode: + return "Export"; + case kStartSectionCode: + return "Start"; + case kCodeSectionCode: + return "Code"; + case kElementSectionCode: + return "Element"; + case kDataSectionCode: + return "Data"; + case kNameSectionCode: + return "Name"; + default: + return "<unknown>"; + } +} + namespace { const char* kNameString = "name"; const size_t kNameStringLength = 4; -LocalType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { +ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { switch (expr.kind) { case WasmInitExpr::kNone: - return kAstStmt; + return kWasmStmt; case WasmInitExpr::kGlobalIndex: return expr.val.global_index < module->globals.size() ? module->globals[expr.val.global_index].type - : kAstStmt; + : kWasmStmt; case WasmInitExpr::kI32Const: - return kAstI32; + return kWasmI32; case WasmInitExpr::kI64Const: - return kAstI64; + return kWasmI64; case WasmInitExpr::kF32Const: - return kAstF32; + return kWasmF32; case WasmInitExpr::kF64Const: - return kAstF64; + return kWasmF64; default: UNREACHABLE(); - return kAstStmt; + return kWasmStmt; } } @@ -177,19 +214,21 @@ class ModuleDecoder : public Decoder { public: ModuleDecoder(Zone* zone, const byte* module_start, const byte* module_end, ModuleOrigin origin) - : Decoder(module_start, module_end), module_zone(zone), origin_(origin) { + : Decoder(module_start, module_end), + module_zone(zone), + origin_(FLAG_assume_asmjs_origin ? kAsmJsOrigin : origin) { result_.start = start_; - if (limit_ < start_) { + if (end_ < start_) { error(start_, "end is less than start"); - limit_ = start_; + end_ = start_; } } virtual void onFirstError() { - pc_ = limit_; // On error, terminate section decoding loop. + pc_ = end_; // On error, terminate section decoding loop. } - static void DumpModule(WasmModule* module, const ModuleResult& result) { + void DumpModule(const ModuleResult& result) { std::string path; if (FLAG_dump_wasm_module_path) { path = FLAG_dump_wasm_module_path; @@ -199,7 +238,7 @@ class ModuleDecoder : public Decoder { } } // File are named `HASH.{ok,failed}.wasm`. - size_t hash = base::hash_range(module->module_start, module->module_end); + size_t hash = base::hash_range(start_, end_); char buf[32] = {'\0'}; #if V8_OS_WIN && _MSC_VER < 1900 #define snprintf sprintf_s @@ -208,17 +247,15 @@ class ModuleDecoder : public Decoder { result.ok() ? "ok" : "failed"); std::string name(buf); if (FILE* wasm_file = base::OS::FOpen((path + name).c_str(), "wb")) { - fwrite(module->module_start, module->module_end - module->module_start, 1, - wasm_file); + fwrite(start_, end_ - start_, 1, wasm_file); fclose(wasm_file); } } // Decodes an entire module. - ModuleResult DecodeModule(WasmModule* module, bool verify_functions = true) { + ModuleResult DecodeModule(bool verify_functions = true) { pc_ = start_; - module->module_start = start_; - module->module_end = limit_; + WasmModule* module = new WasmModule(module_zone); module->min_mem_pages = 0; module->max_mem_pages = 0; module->mem_export = false; @@ -249,8 +286,8 @@ class ModuleDecoder : public Decoder { // ===== Type section ==================================================== if (section_iter.section_code() == kTypeSectionCode) { - uint32_t signatures_count = consume_u32v("signatures count"); - module->signatures.reserve(SafeReserve(signatures_count)); + uint32_t signatures_count = consume_count("types count", kV8MaxWasmTypes); + module->signatures.reserve(signatures_count); for (uint32_t i = 0; ok() && i < signatures_count; ++i) { TRACE("DecodeSignature[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); @@ -262,8 +299,9 @@ class ModuleDecoder : public Decoder { // ===== Import section ================================================== if (section_iter.section_code() == kImportSectionCode) { - uint32_t import_table_count = consume_u32v("import table count"); - module->import_table.reserve(SafeReserve(import_table_count)); + uint32_t import_table_count = + consume_count("imports count", kV8MaxWasmImports); + module->import_table.reserve(import_table_count); for (uint32_t i = 0; ok() && i < import_table_count; ++i) { TRACE("DecodeImportTable[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); @@ -280,9 +318,6 @@ class ModuleDecoder : public Decoder { const byte* pos = pc_; import->module_name_offset = consume_string(&import->module_name_length, true); - if (import->module_name_length == 0) { - error(pos, "import module name cannot be NULL"); - } import->field_name_offset = consume_string(&import->field_name_length, true); @@ -307,6 +342,7 @@ class ModuleDecoder : public Decoder { } case kExternalTable: { // ===== Imported table ========================================== + if (!AddTable(module)) break; import->index = static_cast<uint32_t>(module->function_tables.size()); module->function_tables.push_back({0, 0, false, @@ -314,30 +350,29 @@ class ModuleDecoder : public Decoder { false, SignatureMap()}); expect_u8("element type", kWasmAnyFunctionTypeForm); WasmIndirectFunctionTable* table = &module->function_tables.back(); - consume_resizable_limits( - "element count", "elements", WasmModule::kV8MaxTableSize, - &table->min_size, &table->has_max, WasmModule::kV8MaxTableSize, - &table->max_size); + consume_resizable_limits("element count", "elements", + FLAG_wasm_max_table_size, &table->min_size, + &table->has_max, FLAG_wasm_max_table_size, + &table->max_size); break; } case kExternalMemory: { // ===== Imported memory ========================================= - bool has_max = false; - consume_resizable_limits("memory", "pages", WasmModule::kV8MaxPages, - &module->min_mem_pages, &has_max, - WasmModule::kSpecMaxPages, - &module->max_mem_pages); - module->has_memory = true; + if (!AddMemory(module)) break; + consume_resizable_limits( + "memory", "pages", FLAG_wasm_max_mem_pages, + &module->min_mem_pages, &module->has_max_mem, + kSpecMaxWasmMemoryPages, &module->max_mem_pages); break; } case kExternalGlobal: { // ===== Imported global ========================================= import->index = static_cast<uint32_t>(module->globals.size()); module->globals.push_back( - {kAstStmt, false, WasmInitExpr(), 0, true, false}); + {kWasmStmt, false, WasmInitExpr(), 0, true, false}); WasmGlobal* global = &module->globals.back(); global->type = consume_value_type(); - global->mutability = consume_u8("mutability") != 0; + global->mutability = consume_mutability(); if (global->mutability) { error("mutable globals cannot be imported"); } @@ -353,8 +388,9 @@ class ModuleDecoder : public Decoder { // ===== Function section ================================================ if (section_iter.section_code() == kFunctionSectionCode) { - uint32_t functions_count = consume_u32v("functions count"); - module->functions.reserve(SafeReserve(functions_count)); + uint32_t functions_count = + consume_count("functions count", kV8MaxWasmFunctions); + module->functions.reserve(functions_count); module->num_declared_functions = functions_count; for (uint32_t i = 0; ok() && i < functions_count; ++i) { uint32_t func_index = static_cast<uint32_t>(module->functions.size()); @@ -375,23 +411,17 @@ class ModuleDecoder : public Decoder { // ===== Table section =================================================== if (section_iter.section_code() == kTableSectionCode) { - const byte* pos = pc_; - uint32_t table_count = consume_u32v("table count"); - // Require at most one table for now. - if (table_count > 1) { - error(pos, pos, "invalid table count %d, maximum 1", table_count); - } - if (module->function_tables.size() < 1) { - module->function_tables.push_back({0, 0, false, std::vector<int32_t>(), - false, false, SignatureMap()}); - } + uint32_t table_count = consume_count("table count", kV8MaxWasmTables); for (uint32_t i = 0; ok() && i < table_count; i++) { + if (!AddTable(module)) break; + module->function_tables.push_back({0, 0, false, std::vector<int32_t>(), + false, false, SignatureMap()}); WasmIndirectFunctionTable* table = &module->function_tables.back(); expect_u8("table type", kWasmAnyFunctionTypeForm); consume_resizable_limits("table elements", "elements", - WasmModule::kV8MaxTableSize, &table->min_size, - &table->has_max, WasmModule::kV8MaxTableSize, + FLAG_wasm_max_table_size, &table->min_size, + &table->has_max, FLAG_wasm_max_table_size, &table->max_size); } section_iter.advance(); @@ -399,39 +429,30 @@ class ModuleDecoder : public Decoder { // ===== Memory section ================================================== if (section_iter.section_code() == kMemorySectionCode) { - const byte* pos = pc_; - uint32_t memory_count = consume_u32v("memory count"); - // Require at most one memory for now. - if (memory_count > 1) { - error(pos, pos, "invalid memory count %d, maximum 1", memory_count); - } + uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories); for (uint32_t i = 0; ok() && i < memory_count; i++) { - bool has_max = false; - consume_resizable_limits( - "memory", "pages", WasmModule::kV8MaxPages, &module->min_mem_pages, - &has_max, WasmModule::kSpecMaxPages, &module->max_mem_pages); + if (!AddMemory(module)) break; + consume_resizable_limits("memory", "pages", FLAG_wasm_max_mem_pages, + &module->min_mem_pages, &module->has_max_mem, + kSpecMaxWasmMemoryPages, + &module->max_mem_pages); } - module->has_memory = true; section_iter.advance(); } // ===== Global section ================================================== if (section_iter.section_code() == kGlobalSectionCode) { - uint32_t globals_count = consume_u32v("globals count"); + uint32_t globals_count = + consume_count("globals count", kV8MaxWasmGlobals); uint32_t imported_globals = static_cast<uint32_t>(module->globals.size()); - if (!IsWithinLimit(std::numeric_limits<int32_t>::max(), globals_count, - imported_globals)) { - error(pos, pos, "too many imported+defined globals: %u + %u", - imported_globals, globals_count); - } - module->globals.reserve(SafeReserve(imported_globals + globals_count)); + module->globals.reserve(imported_globals + globals_count); for (uint32_t i = 0; ok() && i < globals_count; ++i) { TRACE("DecodeGlobal[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); // Add an uninitialized global and pass a pointer to it. module->globals.push_back( - {kAstStmt, false, WasmInitExpr(), 0, false, false}); + {kWasmStmt, false, WasmInitExpr(), 0, false, false}); WasmGlobal* global = &module->globals.back(); DecodeGlobalInModule(module, i + imported_globals, global); } @@ -440,8 +461,9 @@ class ModuleDecoder : public Decoder { // ===== Export section ================================================== if (section_iter.section_code() == kExportSectionCode) { - uint32_t export_table_count = consume_u32v("export table count"); - module->export_table.reserve(SafeReserve(export_table_count)); + uint32_t export_table_count = + consume_count("exports count", kV8MaxWasmImports); + module->export_table.reserve(export_table_count); for (uint32_t i = 0; ok() && i < export_table_count; ++i) { TRACE("DecodeExportTable[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); @@ -473,7 +495,11 @@ class ModuleDecoder : public Decoder { } case kExternalMemory: { uint32_t index = consume_u32v("memory index"); - if (index != 0) error("invalid memory index != 0"); + // TODO(titzer): This should become more regular + // once we support multiple memories. + if (!module->has_memory || index != 0) { + error("invalid memory index != 0"); + } module->mem_export = true; break; } @@ -493,8 +519,8 @@ class ModuleDecoder : public Decoder { break; } } - // Check for duplicate exports. - if (ok() && module->export_table.size() > 1) { + // Check for duplicate exports (except for asm.js). + if (ok() && origin_ != kAsmJsOrigin && module->export_table.size() > 1) { std::vector<WasmExport> sorted_exports(module->export_table); const byte* base = start_; auto cmp_less = [base](const WasmExport& a, const WasmExport& b) { @@ -538,7 +564,8 @@ class ModuleDecoder : public Decoder { // ===== Elements section ================================================ if (section_iter.section_code() == kElementSectionCode) { - uint32_t element_count = consume_u32v("element count"); + uint32_t element_count = + consume_count("element count", FLAG_wasm_max_table_size); for (uint32_t i = 0; ok() && i < element_count; ++i) { const byte* pos = pc(); uint32_t table_index = consume_u32v("table index"); @@ -551,19 +578,18 @@ class ModuleDecoder : public Decoder { } else { table = &module->function_tables[table_index]; } - WasmInitExpr offset = consume_init_expr(module, kAstI32); - uint32_t num_elem = consume_u32v("number of elements"); + WasmInitExpr offset = consume_init_expr(module, kWasmI32); + uint32_t num_elem = + consume_count("number of elements", kV8MaxWasmTableEntries); std::vector<uint32_t> vector; module->table_inits.push_back({table_index, offset, vector}); WasmTableInit* init = &module->table_inits.back(); - init->entries.reserve(SafeReserve(num_elem)); for (uint32_t j = 0; ok() && j < num_elem; j++) { WasmFunction* func = nullptr; uint32_t index = consume_func_index(module, &func); init->entries.push_back(index); if (table && index < module->functions.size()) { // Canonicalize signature indices during decoding. - // TODO(titzer): suboptimal, redundant when verifying only. table->map.FindOrInsert(module->functions[index].sig); } } @@ -587,10 +613,8 @@ class ModuleDecoder : public Decoder { function->code_start_offset = pc_offset(); function->code_end_offset = pc_offset() + size; if (verify_functions) { - ModuleEnv module_env; - module_env.module = module; - module_env.origin = module->origin; - + ModuleBytesEnv module_env(module, nullptr, + ModuleWireBytes(start_, end_)); VerifyFunctionBody(i + module->num_imported_functions, &module_env, function); } @@ -601,8 +625,9 @@ class ModuleDecoder : public Decoder { // ===== Data section ==================================================== if (section_iter.section_code() == kDataSectionCode) { - uint32_t data_segments_count = consume_u32v("data segments count"); - module->data_segments.reserve(SafeReserve(data_segments_count)); + uint32_t data_segments_count = + consume_count("data segments count", kV8MaxWasmDataSegments); + module->data_segments.reserve(data_segments_count); for (uint32_t i = 0; ok() && i < data_segments_count; ++i) { if (!module->has_memory) { error("cannot load data without memory"); @@ -623,22 +648,29 @@ class ModuleDecoder : public Decoder { // ===== Name section ==================================================== if (section_iter.section_code() == kNameSectionCode) { - uint32_t functions_count = consume_u32v("functions count"); + // TODO(titzer): find a way to report name errors as warnings. + // Use an inner decoder so that errors don't fail the outer decoder. + Decoder inner(start_, pc_, end_); + uint32_t functions_count = inner.consume_u32v("functions count"); - for (uint32_t i = 0; ok() && i < functions_count; ++i) { + for (uint32_t i = 0; inner.ok() && i < functions_count; ++i) { uint32_t function_name_length = 0; - uint32_t name_offset = consume_string(&function_name_length, false); + uint32_t name_offset = + consume_string(inner, &function_name_length, false); uint32_t func_index = i; - if (func_index < module->functions.size()) { + if (inner.ok() && func_index < module->functions.size()) { module->functions[func_index].name_offset = name_offset; module->functions[func_index].name_length = function_name_length; } - uint32_t local_names_count = consume_u32v("local names count"); - for (uint32_t j = 0; ok() && j < local_names_count; j++) { - skip_string(); + uint32_t local_names_count = inner.consume_u32v("local names count"); + for (uint32_t j = 0; inner.ok() && j < local_names_count; j++) { + uint32_t length = inner.consume_u32v("string length"); + inner.consume_bytes(length, "string"); } } + // Skip the whole names section in the outer decoder. + consume_bytes(section_iter.payload_length(), nullptr); section_iter.advance(); } @@ -656,25 +688,19 @@ class ModuleDecoder : public Decoder { if (verify_functions && result.ok()) { result.MoveFrom(result_); // Copy error code and location. } - if (FLAG_dump_wasm_module) DumpModule(module, result); + if (FLAG_dump_wasm_module) DumpModule(result); return result; } - uint32_t SafeReserve(uint32_t count) { - // Avoid OOM by only reserving up to a certain size. - const uint32_t kMaxReserve = 20000; - return count < kMaxReserve ? count : kMaxReserve; - } - // Decodes a single anonymous function starting at {start_}. - FunctionResult DecodeSingleFunction(ModuleEnv* module_env, + FunctionResult DecodeSingleFunction(ModuleBytesEnv* module_env, WasmFunction* function) { pc_ = start_; function->sig = consume_sig(); // read signature function->name_offset = 0; // ---- name function->name_length = 0; // ---- name length function->code_start_offset = off(pc_); // ---- code start - function->code_end_offset = off(limit_); // ---- code end + function->code_end_offset = off(end_); // ---- code end if (ok()) VerifyFunctionBody(0, module_env, function); @@ -693,7 +719,7 @@ class ModuleDecoder : public Decoder { WasmInitExpr DecodeInitExpr(const byte* start) { pc_ = start; - return consume_init_expr(nullptr, kAstStmt); + return consume_init_expr(nullptr, kWasmStmt); } private: @@ -703,13 +729,32 @@ class ModuleDecoder : public Decoder { uint32_t off(const byte* ptr) { return static_cast<uint32_t>(ptr - start_); } + bool AddTable(WasmModule* module) { + if (module->function_tables.size() > 0) { + error("At most one table is supported"); + return false; + } else { + return true; + } + } + + bool AddMemory(WasmModule* module) { + if (module->has_memory) { + error("At most one memory is supported"); + return false; + } else { + module->has_memory = true; + return true; + } + } + // Decodes a single global entry inside a module starting at {pc_}. void DecodeGlobalInModule(WasmModule* module, uint32_t index, WasmGlobal* global) { global->type = consume_value_type(); - global->mutability = consume_u8("mutability") != 0; + global->mutability = consume_mutability(); const byte* pos = pc(); - global->init = consume_init_expr(module, kAstStmt); + global->init = consume_init_expr(module, kWasmStmt); switch (global->init.kind) { case WasmInitExpr::kGlobalIndex: { uint32_t other_index = global->init.val.global_index; @@ -747,12 +792,12 @@ class ModuleDecoder : public Decoder { void DecodeDataSegmentInModule(WasmModule* module, WasmDataSegment* segment) { const byte* start = pc_; expect_u8("linear memory index", 0); - segment->dest_addr = consume_init_expr(module, kAstI32); + segment->dest_addr = consume_init_expr(module, kWasmI32); segment->source_size = consume_u32v("source size"); segment->source_offset = static_cast<uint32_t>(pc_ - start_); // Validate the data is in the module. - uint32_t module_limit = static_cast<uint32_t>(limit_ - start_); + uint32_t module_limit = static_cast<uint32_t>(end_ - start_); if (!IsWithinLimit(module_limit, segment->source_offset, segment->source_size)) { error(start, "segment out of bounds of module"); @@ -779,21 +824,24 @@ class ModuleDecoder : public Decoder { } // Verifies the body (code) of a given function. - void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv, + void VerifyFunctionBody(uint32_t func_num, ModuleBytesEnv* menv, WasmFunction* function) { + WasmFunctionName func_name(function, + menv->wire_bytes.GetNameOrNull(function)); if (FLAG_trace_wasm_decoder || FLAG_trace_wasm_decode_time) { OFStream os(stdout); - os << "Verifying WASM function " << WasmFunctionName(function, menv) - << std::endl; + os << "Verifying WASM function " << func_name << std::endl; } - FunctionBody body = {menv, function->sig, start_, + FunctionBody body = {function->sig, start_, start_ + function->code_start_offset, start_ + function->code_end_offset}; - DecodeResult result = VerifyWasmCode(module_zone->allocator(), body); + DecodeResult result = VerifyWasmCode( + module_zone->allocator(), + menv == nullptr ? nullptr : menv->module_env.module, body); if (result.failed()) { // Wrap the error message from the function decoder. std::ostringstream str; - str << "in function " << WasmFunctionName(function, menv) << ": "; + str << "in function " << func_name << ": "; str << result; std::string strval = str.str(); const char* raw = strval.c_str(); @@ -808,27 +856,26 @@ class ModuleDecoder : public Decoder { } } + uint32_t consume_string(uint32_t* length, bool validate_utf8) { + return consume_string(*this, length, validate_utf8); + } + // Reads a length-prefixed string, checking that it is within bounds. Returns // the offset of the string, and the length as an out parameter. - uint32_t consume_string(uint32_t* length, bool validate_utf8) { - *length = consume_u32v("string length"); - uint32_t offset = pc_offset(); - const byte* string_start = pc_; + uint32_t consume_string(Decoder& decoder, uint32_t* length, + bool validate_utf8) { + *length = decoder.consume_u32v("string length"); + uint32_t offset = decoder.pc_offset(); + const byte* string_start = decoder.pc(); // Consume bytes before validation to guarantee that the string is not oob. - if (*length > 0) consume_bytes(*length, "string"); - if (ok() && validate_utf8 && + if (*length > 0) decoder.consume_bytes(*length, "string"); + if (decoder.ok() && validate_utf8 && !unibrow::Utf8::Validate(string_start, *length)) { - error(string_start, "no valid UTF-8 string"); + decoder.error(string_start, "no valid UTF-8 string"); } return offset; } - // Skips over a length-prefixed string, but checks that it is within bounds. - void skip_string() { - uint32_t length = consume_u32v("string length"); - consume_bytes(length, "string"); - } - uint32_t consume_sig_index(WasmModule* module, FunctionSig** sig) { const byte* pos = pc_; uint32_t sig_index = consume_u32v("signature index"); @@ -842,6 +889,17 @@ class ModuleDecoder : public Decoder { return sig_index; } + uint32_t consume_count(const char* name, size_t maximum) { + const byte* p = pc_; + uint32_t count = consume_u32v(name); + if (count > maximum) { + error(p, p, "%s of %u exceeds internal limit of %zu", name, count, + maximum); + return static_cast<uint32_t>(maximum); + } + return count; + } + uint32_t consume_func_index(WasmModule* module, WasmFunction** func) { return consume_index("function index", module->functions, func); } @@ -912,7 +970,7 @@ class ModuleDecoder : public Decoder { return true; } - WasmInitExpr consume_init_expr(WasmModule* module, LocalType expected) { + WasmInitExpr consume_init_expr(WasmModule* module, ValueType expected) { const byte* pos = pc(); uint8_t opcode = consume_u8("opcode"); WasmInitExpr expr; @@ -978,7 +1036,7 @@ class ModuleDecoder : public Decoder { if (!expect_u8("end opcode", kExprEnd)) { expr.kind = WasmInitExpr::kNone; } - if (expected != kAstStmt && TypeOf(module, expr) != kAstI32) { + if (expected != kWasmStmt && TypeOf(module, expr) != kWasmI32) { error(pos, pos, "type error in init expression, expected %s, got %s", WasmOpcodes::TypeName(expected), WasmOpcodes::TypeName(TypeOf(module, expr))); @@ -986,29 +1044,43 @@ class ModuleDecoder : public Decoder { return expr; } + // Read a mutability flag + bool consume_mutability() { + byte val = consume_u8("mutability"); + if (val > 1) error(pc_ - 1, "invalid mutability"); + return val != 0; + } + // Reads a single 8-bit integer, interpreting it as a local type. - LocalType consume_value_type() { + ValueType consume_value_type() { byte val = consume_u8("value type"); - LocalTypeCode t = static_cast<LocalTypeCode>(val); + ValueTypeCode t = static_cast<ValueTypeCode>(val); switch (t) { case kLocalI32: - return kAstI32; + return kWasmI32; case kLocalI64: - return kAstI64; + return kWasmI64; case kLocalF32: - return kAstF32; + return kWasmF32; case kLocalF64: - return kAstF64; - case kLocalS128: + return kWasmF64; + default: if (origin_ != kAsmJsOrigin && FLAG_wasm_simd_prototype) { - return kAstS128; - } else { - error(pc_ - 1, "invalid local type"); - return kAstStmt; + switch (t) { + case kLocalS128: + return kWasmS128; + case kLocalS1x4: + return kWasmS1x4; + case kLocalS1x8: + return kWasmS1x8; + case kLocalS1x16: + return kWasmS1x16; + default: + break; + } } - default: error(pc_ - 1, "invalid local type"); - return kAstStmt; + return kWasmStmt; } } @@ -1016,35 +1088,32 @@ class ModuleDecoder : public Decoder { FunctionSig* consume_sig() { if (!expect_u8("type form", kWasmFunctionTypeForm)) return nullptr; // parse parameter types - uint32_t param_count = consume_u32v("param count"); - std::vector<LocalType> params; + uint32_t param_count = + consume_count("param count", kV8MaxWasmFunctionParams); + if (failed()) return nullptr; + std::vector<ValueType> params; for (uint32_t i = 0; ok() && i < param_count; ++i) { - LocalType param = consume_value_type(); + ValueType param = consume_value_type(); params.push_back(param); } // parse return types - const byte* pt = pc_; - uint32_t return_count = consume_u32v("return count"); - if (return_count > kMaxReturnCount) { - error(pt, pt, "return count of %u exceeds maximum of %u", return_count, - kMaxReturnCount); - return nullptr; - } - std::vector<LocalType> returns; + const size_t max_return_count = FLAG_wasm_mv_prototype + ? kV8MaxWasmFunctionMultiReturns + : kV8MaxWasmFunctionReturns; + uint32_t return_count = consume_count("return count", max_return_count); + if (failed()) return nullptr; + std::vector<ValueType> returns; for (uint32_t i = 0; ok() && i < return_count; ++i) { - LocalType ret = consume_value_type(); + ValueType ret = consume_value_type(); returns.push_back(ret); } - if (failed()) { - // Decoding failed, return void -> void - return new (module_zone) FunctionSig(0, 0, nullptr); - } + if (failed()) return nullptr; // FunctionSig stores the return types first. - LocalType* buffer = - module_zone->NewArray<LocalType>(param_count + return_count); + ValueType* buffer = + module_zone->NewArray<ValueType>(param_count + return_count); uint32_t b = 0; for (uint32_t i = 0; i < return_count; ++i) buffer[b++] = returns[i]; for (uint32_t i = 0; i < param_count; ++i) buffer[b++] = params[i]; @@ -1113,16 +1182,16 @@ ModuleResult DecodeWasmModule(Isolate* isolate, const byte* module_start, isolate->counters()->wasm_decode_module_time()); size_t size = module_end - module_start; if (module_start > module_end) return ModuleError("start > end"); - if (size >= kMaxModuleSize) return ModuleError("size > maximum module size"); + if (size >= kV8MaxWasmModuleSize) + return ModuleError("size > maximum module size"); // TODO(bradnelson): Improve histogram handling of size_t. isolate->counters()->wasm_module_size_bytes()->AddSample( static_cast<int>(size)); // Signatures are stored in zone memory, which have the same lifetime // as the {module}. Zone* zone = new Zone(isolate->allocator(), ZONE_NAME); - WasmModule* module = new WasmModule(zone, module_start); ModuleDecoder decoder(zone, module_start, module_end, origin); - ModuleResult result = decoder.DecodeModule(module, verify_functions); + ModuleResult result = decoder.DecodeModule(verify_functions); // TODO(bradnelson): Improve histogram handling of size_t. // TODO(titzer): this isn't accurate, since it doesn't count the data // allocated on the C++ heap. @@ -1146,14 +1215,14 @@ WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end) { } FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, - ModuleEnv* module_env, + ModuleBytesEnv* module_env, const byte* function_start, const byte* function_end) { HistogramTimerScope wasm_decode_function_time_scope( isolate->counters()->wasm_decode_function_time()); size_t size = function_end - function_start; if (function_start > function_end) return FunctionError("start > end"); - if (size > kMaxFunctionSize) + if (size > kV8MaxWasmFunctionSize) return FunctionError("size > maximum function size"); isolate->counters()->wasm_function_size_bytes()->AddSample( static_cast<int>(size)); @@ -1185,7 +1254,7 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start, for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) { uint32_t size = decoder.consume_u32v("body size"); int offset = static_cast<int>(section_offset + decoder.pc_offset()); - table.push_back(std::make_pair(offset, static_cast<int>(size))); + table.emplace_back(offset, static_cast<int>(size)); DCHECK(table.back().first >= 0 && table.back().second >= 0); decoder.consume_bytes(size); } @@ -1208,22 +1277,31 @@ AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start, for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) { uint32_t size = decoder.consume_u32v("table size"); if (size == 0) { - table.push_back(std::vector<std::pair<int, int>>()); + table.emplace_back(); continue; } if (!decoder.checkAvailable(size)) { decoder.error("illegal asm function offset table size"); } const byte* table_end = decoder.pc() + size; - uint32_t locals_size = decoder.consume_u32("locals size"); + uint32_t locals_size = decoder.consume_u32v("locals size"); + int function_start_position = decoder.consume_u32v("function start pos"); int last_byte_offset = locals_size; - int last_asm_position = 0; - std::vector<std::pair<int, int>> func_asm_offsets; + int last_asm_position = function_start_position; + std::vector<AsmJsOffsetEntry> func_asm_offsets; func_asm_offsets.reserve(size / 4); // conservative estimation + // Add an entry for the stack check, associated with position 0. + func_asm_offsets.push_back( + {0, function_start_position, function_start_position}); while (decoder.ok() && decoder.pc() < table_end) { last_byte_offset += decoder.consume_u32v("byte offset delta"); - last_asm_position += decoder.consume_i32v("asm position delta"); - func_asm_offsets.push_back({last_byte_offset, last_asm_position}); + int call_position = + last_asm_position + decoder.consume_i32v("call position delta"); + int to_number_position = + call_position + decoder.consume_i32v("to_number position delta"); + last_asm_position = to_number_position; + func_asm_offsets.push_back( + {last_byte_offset, call_position, to_number_position}); } if (decoder.pc() != table_end) { decoder.error("broken asm offset table"); @@ -1235,6 +1313,36 @@ AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start, return decoder.toResult(std::move(table)); } +std::vector<CustomSectionOffset> DecodeCustomSections(const byte* start, + const byte* end) { + Decoder decoder(start, end); + decoder.consume_bytes(4, "wasm magic"); + decoder.consume_bytes(4, "wasm version"); + + std::vector<CustomSectionOffset> result; + + while (decoder.more()) { + byte section_code = decoder.consume_u8("section code"); + uint32_t section_length = decoder.consume_u32v("section length"); + uint32_t section_start = decoder.pc_offset(); + if (section_code != 0) { + // Skip known sections. + decoder.consume_bytes(section_length, "section bytes"); + continue; + } + uint32_t name_length = decoder.consume_u32v("name length"); + uint32_t name_offset = decoder.pc_offset(); + decoder.consume_bytes(name_length, "section name"); + uint32_t payload_offset = decoder.pc_offset(); + uint32_t payload_length = section_length - (payload_offset - section_start); + decoder.consume_bytes(payload_length); + result.push_back({section_start, name_offset, name_length, payload_offset, + payload_length, section_length}); + } + + return result; +} + } // namespace wasm } // namespace internal } // namespace v8 |