Commit 058939ab authored by titzer's avatar titzer Committed by Commit bot

[wasm] Enforce limits for maximums for many WebAssembly binary entities.

This CL moves even more limits to wasm-limits.h and enforces limits for
types, functions, parameter counts, return counts, local counts, imports,
globals, and exports.

R=clemensh@chromium.org, ahaas@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2574133002
Cr-Commit-Position: refs/heads/master@{#41699}
parent f3b7f21c
...@@ -590,7 +590,7 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -590,7 +590,7 @@ class WasmFullDecoder : public WasmDecoder {
TRACE("local decls count: %u\n", entries); TRACE("local decls count: %u\n", entries);
while (entries-- > 0 && pc_ < limit_) { while (entries-- > 0 && pc_ < limit_) {
uint32_t count = consume_u32v("local count"); uint32_t count = consume_u32v("local count");
if (count > kMaxNumWasmLocals) { if ((count + local_type_vec_.size()) > kMaxNumWasmLocals) {
error(pc_ - 1, "local count too large"); error(pc_ - 1, "local count too large");
return; return;
} }
......
...@@ -248,8 +248,8 @@ class ModuleDecoder : public Decoder { ...@@ -248,8 +248,8 @@ class ModuleDecoder : public Decoder {
// ===== Type section ==================================================== // ===== Type section ====================================================
if (section_iter.section_code() == kTypeSectionCode) { if (section_iter.section_code() == kTypeSectionCode) {
uint32_t signatures_count = consume_u32v("signatures count"); uint32_t signatures_count = consume_count("types count", kV8MaxWasmTypes);
module->signatures.reserve(SafeReserve(signatures_count)); module->signatures.reserve(signatures_count);
for (uint32_t i = 0; ok() && i < signatures_count; ++i) { for (uint32_t i = 0; ok() && i < signatures_count; ++i) {
TRACE("DecodeSignature[%d] module+%d\n", i, TRACE("DecodeSignature[%d] module+%d\n", i,
static_cast<int>(pc_ - start_)); static_cast<int>(pc_ - start_));
...@@ -261,8 +261,9 @@ class ModuleDecoder : public Decoder { ...@@ -261,8 +261,9 @@ class ModuleDecoder : public Decoder {
// ===== Import section ================================================== // ===== Import section ==================================================
if (section_iter.section_code() == kImportSectionCode) { if (section_iter.section_code() == kImportSectionCode) {
uint32_t import_table_count = consume_u32v("import table count"); uint32_t import_table_count =
module->import_table.reserve(SafeReserve(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) { for (uint32_t i = 0; ok() && i < import_table_count; ++i) {
TRACE("DecodeImportTable[%d] module+%d\n", i, TRACE("DecodeImportTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_)); static_cast<int>(pc_ - start_));
...@@ -352,8 +353,9 @@ class ModuleDecoder : public Decoder { ...@@ -352,8 +353,9 @@ class ModuleDecoder : public Decoder {
// ===== Function section ================================================ // ===== Function section ================================================
if (section_iter.section_code() == kFunctionSectionCode) { if (section_iter.section_code() == kFunctionSectionCode) {
uint32_t functions_count = consume_u32v("functions count"); uint32_t functions_count =
module->functions.reserve(SafeReserve(functions_count)); consume_count("functions count", kV8MaxWasmFunctions);
module->functions.reserve(functions_count);
module->num_declared_functions = functions_count; module->num_declared_functions = functions_count;
for (uint32_t i = 0; ok() && i < functions_count; ++i) { for (uint32_t i = 0; ok() && i < functions_count; ++i) {
uint32_t func_index = static_cast<uint32_t>(module->functions.size()); uint32_t func_index = static_cast<uint32_t>(module->functions.size());
...@@ -374,12 +376,7 @@ class ModuleDecoder : public Decoder { ...@@ -374,12 +376,7 @@ class ModuleDecoder : public Decoder {
// ===== Table section =================================================== // ===== Table section ===================================================
if (section_iter.section_code() == kTableSectionCode) { if (section_iter.section_code() == kTableSectionCode) {
const byte* pos = pc_; uint32_t table_count = consume_count("table count", kV8MaxWasmTables);
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) { if (module->function_tables.size() < 1) {
module->function_tables.push_back({0, 0, false, std::vector<int32_t>(), module->function_tables.push_back({0, 0, false, std::vector<int32_t>(),
false, false, SignatureMap()}); false, false, SignatureMap()});
...@@ -397,12 +394,7 @@ class ModuleDecoder : public Decoder { ...@@ -397,12 +394,7 @@ class ModuleDecoder : public Decoder {
// ===== Memory section ================================================== // ===== Memory section ==================================================
if (section_iter.section_code() == kMemorySectionCode) { if (section_iter.section_code() == kMemorySectionCode) {
const byte* pos = pc_; uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories);
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);
}
for (uint32_t i = 0; ok() && i < memory_count; i++) { for (uint32_t i = 0; ok() && i < memory_count; i++) {
bool has_max = false; bool has_max = false;
...@@ -416,14 +408,10 @@ class ModuleDecoder : public Decoder { ...@@ -416,14 +408,10 @@ class ModuleDecoder : public Decoder {
// ===== Global section ================================================== // ===== Global section ==================================================
if (section_iter.section_code() == kGlobalSectionCode) { 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()); uint32_t imported_globals = static_cast<uint32_t>(module->globals.size());
if (!IsWithinLimit(std::numeric_limits<int32_t>::max(), globals_count, module->globals.reserve(imported_globals + 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));
for (uint32_t i = 0; ok() && i < globals_count; ++i) { for (uint32_t i = 0; ok() && i < globals_count; ++i) {
TRACE("DecodeGlobal[%d] module+%d\n", i, TRACE("DecodeGlobal[%d] module+%d\n", i,
static_cast<int>(pc_ - start_)); static_cast<int>(pc_ - start_));
...@@ -438,8 +426,9 @@ class ModuleDecoder : public Decoder { ...@@ -438,8 +426,9 @@ class ModuleDecoder : public Decoder {
// ===== Export section ================================================== // ===== Export section ==================================================
if (section_iter.section_code() == kExportSectionCode) { if (section_iter.section_code() == kExportSectionCode) {
uint32_t export_table_count = consume_u32v("export table count"); uint32_t export_table_count =
module->export_table.reserve(SafeReserve(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) { for (uint32_t i = 0; ok() && i < export_table_count; ++i) {
TRACE("DecodeExportTable[%d] module+%d\n", i, TRACE("DecodeExportTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_)); static_cast<int>(pc_ - start_));
...@@ -536,7 +525,8 @@ class ModuleDecoder : public Decoder { ...@@ -536,7 +525,8 @@ class ModuleDecoder : public Decoder {
// ===== Elements section ================================================ // ===== Elements section ================================================
if (section_iter.section_code() == kElementSectionCode) { if (section_iter.section_code() == kElementSectionCode) {
uint32_t element_count = consume_u32v("element count"); uint32_t element_count =
consume_count("element count", kV8MaxWasmTableSize);
for (uint32_t i = 0; ok() && i < element_count; ++i) { for (uint32_t i = 0; ok() && i < element_count; ++i) {
const byte* pos = pc(); const byte* pos = pc();
uint32_t table_index = consume_u32v("table index"); uint32_t table_index = consume_u32v("table index");
...@@ -554,7 +544,6 @@ class ModuleDecoder : public Decoder { ...@@ -554,7 +544,6 @@ class ModuleDecoder : public Decoder {
std::vector<uint32_t> vector; std::vector<uint32_t> vector;
module->table_inits.push_back({table_index, offset, vector}); module->table_inits.push_back({table_index, offset, vector});
WasmTableInit* init = &module->table_inits.back(); WasmTableInit* init = &module->table_inits.back();
init->entries.reserve(SafeReserve(num_elem));
for (uint32_t j = 0; ok() && j < num_elem; j++) { for (uint32_t j = 0; ok() && j < num_elem; j++) {
WasmFunction* func = nullptr; WasmFunction* func = nullptr;
uint32_t index = consume_func_index(module, &func); uint32_t index = consume_func_index(module, &func);
...@@ -597,8 +586,9 @@ class ModuleDecoder : public Decoder { ...@@ -597,8 +586,9 @@ class ModuleDecoder : public Decoder {
// ===== Data section ==================================================== // ===== Data section ====================================================
if (section_iter.section_code() == kDataSectionCode) { if (section_iter.section_code() == kDataSectionCode) {
uint32_t data_segments_count = consume_u32v("data segments count"); uint32_t data_segments_count =
module->data_segments.reserve(SafeReserve(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) { for (uint32_t i = 0; ok() && i < data_segments_count; ++i) {
if (!module->has_memory) { if (!module->has_memory) {
error("cannot load data without memory"); error("cannot load data without memory");
...@@ -656,12 +646,6 @@ class ModuleDecoder : public Decoder { ...@@ -656,12 +646,6 @@ class ModuleDecoder : public Decoder {
return 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_}. // Decodes a single anonymous function starting at {start_}.
FunctionResult DecodeSingleFunction(ModuleBytesEnv* module_env, FunctionResult DecodeSingleFunction(ModuleBytesEnv* module_env,
WasmFunction* function) { WasmFunction* function) {
...@@ -838,6 +822,17 @@ class ModuleDecoder : public Decoder { ...@@ -838,6 +822,17 @@ class ModuleDecoder : public Decoder {
return sig_index; 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) { uint32_t consume_func_index(WasmModule* module, WasmFunction** func) {
return consume_index("function index", module->functions, func); return consume_index("function index", module->functions, func);
} }
...@@ -1012,7 +1007,9 @@ class ModuleDecoder : public Decoder { ...@@ -1012,7 +1007,9 @@ class ModuleDecoder : public Decoder {
FunctionSig* consume_sig() { FunctionSig* consume_sig() {
if (!expect_u8("type form", kWasmFunctionTypeForm)) return nullptr; if (!expect_u8("type form", kWasmFunctionTypeForm)) return nullptr;
// parse parameter types // parse parameter types
uint32_t param_count = consume_u32v("param count"); uint32_t param_count =
consume_count("param count", kV8MaxWasmFunctionParams);
if (failed()) return nullptr;
std::vector<LocalType> params; std::vector<LocalType> params;
for (uint32_t i = 0; ok() && i < param_count; ++i) { for (uint32_t i = 0; ok() && i < param_count; ++i) {
LocalType param = consume_value_type(); LocalType param = consume_value_type();
...@@ -1020,23 +1017,18 @@ class ModuleDecoder : public Decoder { ...@@ -1020,23 +1017,18 @@ class ModuleDecoder : public Decoder {
} }
// parse return types // parse return types
const byte* pt = pc_; const size_t max_return_count = FLAG_wasm_mv_prototype
uint32_t return_count = consume_u32v("return count"); ? kV8MaxWasmFunctionMultiReturns
if (return_count > kMaxReturnCount) { : kV8MaxWasmFunctionReturns;
error(pt, pt, "return count of %u exceeds maximum of %u", return_count, uint32_t return_count = consume_count("return count", max_return_count);
kMaxReturnCount); if (failed()) return nullptr;
return nullptr;
}
std::vector<LocalType> returns; std::vector<LocalType> returns;
for (uint32_t i = 0; ok() && i < return_count; ++i) { for (uint32_t i = 0; ok() && i < return_count; ++i) {
LocalType ret = consume_value_type(); LocalType ret = consume_value_type();
returns.push_back(ret); returns.push_back(ret);
} }
if (failed()) { if (failed()) return nullptr;
// Decoding failed, return void -> void
return new (module_zone) FunctionSig(0, 0, nullptr);
}
// FunctionSig stores the return types first. // FunctionSig stores the return types first.
LocalType* buffer = LocalType* buffer =
......
...@@ -9,14 +9,25 @@ namespace v8 { ...@@ -9,14 +9,25 @@ namespace v8 {
namespace internal { namespace internal {
namespace wasm { namespace wasm {
const size_t kV8MaxWasmSignatures = 10000000; // The following limits are imposed by V8 on WebAssembly modules.
const size_t kV8MaxWasmFunctions = 10000000; // The limits are agreed upon with other engines for consistency.
const size_t kV8MaxWasmTypes = 1000000;
const size_t kV8MaxWasmFunctions = 1000000;
const size_t kV8MaxWasmImports = 100000;
const size_t kV8MaxWasmExports = 100000;
const size_t kV8MaxWasmGlobals = 1000000;
const size_t kV8MaxWasmDataSegments = 100000;
const size_t kV8MaxWasmMemoryPages = 16384; // = 1 GiB const size_t kV8MaxWasmMemoryPages = 16384; // = 1 GiB
const size_t kV8MaxWasmStringSize = 256; const size_t kV8MaxWasmStringSize = 256;
const size_t kV8MaxWasmModuleSize = 1024 * 1024 * 1024; // = 1 GiB const size_t kV8MaxWasmModuleSize = 1024 * 1024 * 1024; // = 1 GiB
const size_t kV8MaxWasmFunctionSize = 128 * 1024; const size_t kV8MaxWasmFunctionSize = 128 * 1024;
const size_t kV8MaxWasmFunctionLocals = 50000; const size_t kV8MaxWasmFunctionLocals = 50000;
const size_t kV8MaxWasmFunctionParams = 1000;
const size_t kV8MaxWasmFunctionMultiReturns = 1000;
const size_t kV8MaxWasmFunctionReturns = 1;
const size_t kV8MaxWasmTableSize = 16 * 1024 * 1024; const size_t kV8MaxWasmTableSize = 16 * 1024 * 1024;
const size_t kV8MaxWasmTables = 1;
const size_t kV8MaxWasmMemories = 1;
const size_t kSpecMaxWasmMemoryPages = 65536; const size_t kSpecMaxWasmMemoryPages = 65536;
......
...@@ -62,7 +62,6 @@ inline bool IsValidSectionCode(uint8_t byte) { ...@@ -62,7 +62,6 @@ inline bool IsValidSectionCode(uint8_t byte) {
const char* SectionName(WasmSectionCode code); const char* SectionName(WasmSectionCode code);
// Constants for fixed-size elements within a module. // Constants for fixed-size elements within a module.
static const uint32_t kMaxReturnCount = 1;
static const uint8_t kResizableMaximumFlag = 1; static const uint8_t kResizableMaximumFlag = 1;
static const int32_t kInvalidFunctionIndex = -1; static const int32_t kInvalidFunctionIndex = -1;
......
...@@ -314,13 +314,13 @@ TEST_F(AstDecoderTest, NumLocalAboveLimit) { ...@@ -314,13 +314,13 @@ TEST_F(AstDecoderTest, NumLocalAboveLimit) {
} }
TEST_F(AstDecoderTest, GetLocal_varint) { TEST_F(AstDecoderTest, GetLocal_varint) {
const int kMaxLocals = kMaxNumWasmLocals; const int kMaxLocals = kMaxNumWasmLocals - 1;
AddLocals(kAstI32, kMaxLocals); AddLocals(kAstI32, kMaxLocals);
EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_1(66)); EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_1(66));
EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_2(7777)); EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_2(7777));
EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_3(888888)); EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_3(8888));
EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_4(3999999)); EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_4(9999));
EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_5(kMaxLocals - 1)); EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_5(kMaxLocals - 1));
...@@ -334,6 +334,14 @@ TEST_F(AstDecoderTest, GetLocal_varint) { ...@@ -334,6 +334,14 @@ TEST_F(AstDecoderTest, GetLocal_varint) {
EXPECT_FAILURE(i_v, kExprGetLocal, U32V_4(kMaxLocals + 1)); EXPECT_FAILURE(i_v, kExprGetLocal, U32V_4(kMaxLocals + 1));
} }
TEST_F(AstDecoderTest, GetLocal_toomany) {
AddLocals(kAstI32, kMaxNumWasmLocals - 100);
AddLocals(kAstI32, 100);
EXPECT_VERIFIES(i_v, kExprGetLocal, U32V_1(66));
EXPECT_FAILURE(i_i, kExprGetLocal, U32V_1(66));
}
TEST_F(AstDecoderTest, Binops_off_end) { TEST_F(AstDecoderTest, Binops_off_end) {
byte code1[] = {0}; // [opcode] byte code1[] = {0}; // [opcode]
for (size_t i = 0; i < arraysize(kInt32BinopOpcodes); i++) { for (size_t i = 0; i < arraysize(kInt32BinopOpcodes); i++) {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "src/handles.h" #include "src/handles.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
#include "src/wasm/module-decoder.h" #include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-macro-gen.h" #include "src/wasm/wasm-macro-gen.h"
#include "src/wasm/wasm-opcodes.h" #include "src/wasm/wasm-opcodes.h"
...@@ -310,7 +311,7 @@ TEST_F(WasmModuleVerifyTest, NGlobals) { ...@@ -310,7 +311,7 @@ TEST_F(WasmModuleVerifyTest, NGlobals) {
WASM_INIT_EXPR_F32(7.7), // init WASM_INIT_EXPR_F32(7.7), // init
}; };
for (uint32_t i = 0; i < 1000000; i = i * 13 + 1) { for (uint32_t i = 0; i < kV8MaxWasmGlobals; i = i * 13 + 1) {
std::vector<byte> buffer; std::vector<byte> buffer;
size_t size = SizeOfVarInt(i) + i * sizeof(data); size_t size = SizeOfVarInt(i) + i * sizeof(data);
const byte globals[] = {kGlobalSectionCode, U32V_5(size)}; const byte globals[] = {kGlobalSectionCode, U32V_5(size)};
...@@ -847,6 +848,31 @@ TEST_F(WasmSignatureDecodeTest, Ok_i_tt) { ...@@ -847,6 +848,31 @@ TEST_F(WasmSignatureDecodeTest, Ok_i_tt) {
} }
} }
TEST_F(WasmSignatureDecodeTest, TooManyParams) {
static const byte data[] = {kWasmFunctionTypeForm,
WASM_I32V_3(kV8MaxWasmFunctionParams + 1),
kLocalI32, 0};
FunctionSig* sig =
DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data));
EXPECT_FALSE(sig != nullptr);
}
TEST_F(WasmSignatureDecodeTest, TooManyReturns) {
bool prev = FLAG_wasm_mv_prototype;
for (int i = 0; i < 2; i++) {
FLAG_wasm_mv_prototype = i != 0;
const int max_return_count =
static_cast<int>(FLAG_wasm_mv_prototype ? kV8MaxWasmFunctionMultiReturns
: kV8MaxWasmFunctionReturns);
byte data[] = {kWasmFunctionTypeForm, 0, WASM_I32V_3(max_return_count + 1),
kLocalI32};
FunctionSig* sig =
DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data));
EXPECT_EQ(nullptr, sig);
FLAG_wasm_mv_prototype = prev;
}
}
TEST_F(WasmSignatureDecodeTest, Fail_off_end) { TEST_F(WasmSignatureDecodeTest, Fail_off_end) {
byte data[256]; byte data[256];
for (int p = 0; p <= 255; p = p + 1 + p * 3) { for (int p = 0; p <= 255; p = p + 1 + p * 3) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment