Bug 1324008 - Baldr: tweak internal limits to match other browsers (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Fri, 23 Dec 2016 13:18:03 -0600
changeset 453579 d01c0cd382843219b3ff12dc4a675d32b29809a7
parent 453578 d98d1548d5dd92e6cb5ad6e7d052e12526e59502
child 453580 ec3d424e593ebce4b99c1857cbf74350245c67b6
push id39711
push userdmitchell@mozilla.com
push dateFri, 23 Dec 2016 21:59:47 +0000
reviewersbbouvier
bugs1324008
milestone53.0a1
Bug 1324008 - Baldr: tweak internal limits to match other browsers (r=bbouvier) MozReview-Commit-ID: Dtpn29HeNWV
js/src/jit-test/tests/wasm/regress/too-large-frame.js
js/src/jit/arm/Architecture-arm.h
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmBinaryConstants.h
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmTable.cpp
js/src/wasm/WasmTypes.h
js/src/wasm/WasmValidate.cpp
--- a/js/src/jit-test/tests/wasm/regress/too-large-frame.js
+++ b/js/src/jit-test/tests/wasm/regress/too-large-frame.js
@@ -18,12 +18,12 @@ wasmEvalText(
 }
 
 // The wasm baseline compiler cuts off frames at 256KB at the moment;
 // the test case for bug 1280934 constructed a frame around 512KB so
 // duplicate that here.
 
 function locals() {
     var s = "";
-    for ( var i=0 ; i < 64000 ; i++ )
+    for ( var i=0 ; i < 50000 ; i++ )
         s += "(local f64)\n";
     return s;
 }
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -33,17 +33,17 @@ static const uint32_t ION_FRAME_SLACK_SI
 // These offsets are specific to nunboxing, and capture offsets into the
 // components of a js::Value.
 static const int32_t NUNBOX32_TYPE_OFFSET    = 4;
 static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;
 
 static const uint32_t ShadowStackSpace = 0;
 
 // How far forward/back can a jump go? Provide a generous buffer for thunks.
-static const uint32_t JumpImmediateRange = 25 * 1024 * 1024;
+static const uint32_t JumpImmediateRange = 20 * 1024 * 1024;
 
 ////
 // These offsets are related to bailouts.
 ////
 
 // Size of each bailout table entry. On arm, this is presently a single call
 // (which is wrong!). The call clobbers lr.
 // For now, I've dealt with this by ensuring that we never allocate to lr. It
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1691,17 +1691,17 @@ class MOZ_STACK_CLASS ModuleValidator
     bool addStandardLibrarySimdOpName(const char* name, SimdOperation op) {
         JSAtom* atom = Atomize(cx_, name, strlen(name));
         if (!atom)
             return false;
         return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
     }
     bool newSig(Sig&& sig, uint32_t* sigIndex) {
         *sigIndex = 0;
-        if (mg_.numSigs() >= MaxSigs)
+        if (mg_.numSigs() >= AsmJSMaxTypes)
             return failCurrentOffset("too many signatures");
 
         *sigIndex = mg_.numSigs();
         mg_.initSig(*sigIndex, Move(sig));
         return true;
     }
     bool declareSig(Sig&& sig, uint32_t* sigIndex) {
         SigMap::AddPtr p = sigMap_.lookupForAdd(sig);
@@ -1838,21 +1838,21 @@ class MOZ_STACK_CLASS ModuleValidator
         }
 
         CompileArgs args;
         if (!args.initFromContext(cx_, Move(scriptedCaller)))
             return false;
 
         auto env = MakeUnique<ModuleEnvironment>(ModuleKind::AsmJS);
         if (!env ||
-            !env->sigs.resize(MaxSigs) ||
-            !env->funcSigs.resize(MaxFuncs) ||
+            !env->sigs.resize(AsmJSMaxTypes) ||
+            !env->funcSigs.resize(AsmJSMaxFuncs) ||
             !env->funcImportGlobalDataOffsets.resize(AsmJSMaxImports) ||
-            !env->tables.resize(MaxTables) ||
-            !env->asmJSSigToTableIndex.resize(MaxSigs))
+            !env->tables.resize(AsmJSMaxTables) ||
+            !env->asmJSSigToTableIndex.resize(AsmJSMaxTypes))
         {
             return false;
         }
 
         env->minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
 
         if (!mg_.init(Move(env), args, asmJSMetadata_.get()))
             return false;
@@ -2151,32 +2151,32 @@ class MOZ_STACK_CLASS ModuleValidator
                                                         func.srcBegin() - asmJSMetadata_->srcStart,
                                                         func.srcEnd() - asmJSMetadata_->srcStart);
     }
     bool addFunction(PropertyName* name, uint32_t firstUse, Sig&& sig, Func** func) {
         uint32_t sigIndex;
         if (!declareSig(Move(sig), &sigIndex))
             return false;
         uint32_t funcIndex = AsmJSFirstDefFuncIndex + numFunctions();
-        if (funcIndex >= MaxFuncs)
+        if (funcIndex >= AsmJSMaxFuncs)
             return failCurrentOffset("too many functions");
         mg_.initFuncSig(funcIndex, sigIndex);
         Global* global = validationLifo_.new_<Global>(Global::Function);
         if (!global)
             return false;
         global->u.funcIndex_ = funcIndex;
         if (!globalMap_.putNew(name, global))
             return false;
         *func = validationLifo_.new_<Func>(name, firstUse, funcIndex);
         return *func && functions_.append(*func);
     }
     bool declareFuncPtrTable(Sig&& sig, PropertyName* name, uint32_t firstUse, uint32_t mask,
                              uint32_t* index)
     {
-        if (mask > MaxTableElems)
+        if (mask > MaxTableLength)
             return failCurrentOffset("function pointer table too big");
         uint32_t sigIndex;
         if (!newSig(Move(sig), &sigIndex))
             return false;
         if (!mg_.initSigTableLength(sigIndex, mask + 1))
             return false;
         Global* global = validationLifo_.new_<Global>(Global::FuncPtrTable);
         if (!global)
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -435,12 +435,51 @@ enum class Op
 // or WebAssembly is used.
 
 enum class Telemetry
 {
     ASMJS = 0,
     WASM = 1
 };
 
+// Static offsets into the global data of every module that is compiled.
+
+static const unsigned NaN64GlobalDataOffset  = 0;
+static const unsigned NaN32GlobalDataOffset  = NaN64GlobalDataOffset + sizeof(double);
+static const unsigned InitialGlobalDataBytes = NaN32GlobalDataOffset + sizeof(float);
+
+// These limits are agreed upon with other engines for consistency.
+
+static const unsigned MaxTypes               =  1000000;
+static const unsigned MaxFuncs               =  1000000;
+static const unsigned MaxImports             =   100000;
+static const unsigned MaxExports             =   100000;
+static const unsigned MaxGlobals             =  1000000;
+static const unsigned MaxDataSegments        =   100000;
+static const unsigned MaxElemSegments        = 10000000;
+static const unsigned MaxTableLength         = 10000000;
+static const unsigned MaxStringBytes         =   100000;
+static const unsigned MaxLocals              =    50000;
+static const unsigned MaxParams              =     1000;
+static const unsigned MaxBrTableElems        =  1000000;
+static const unsigned MaxModuleBytes         = 1024 * 1024 * 1024;
+static const unsigned MaxFunctionBytes       =         128 * 1024;
+
+// To be able to assign function indices during compilation while the number of
+// imports is still unknown, asm.js sets a maximum number of imports so it can
+// immediately start handing out function indices starting at the maximum + 1.
+// this means that there is a "hole" between the last import and the first
+// definition, but that's fine.
+
+static const unsigned AsmJSMaxTypes          =   4 * 1024;
+static const unsigned AsmJSMaxFuncs          = 512 * 1024;
+static const unsigned AsmJSMaxImports        =   4 * 1024;
+static const unsigned AsmJSMaxTables         =   4 * 1024;
+static const unsigned AsmJSFirstDefFuncIndex = AsmJSMaxImports + 1;
+
+static_assert(AsmJSMaxTypes <= MaxTypes, "conservative");
+static_assert(AsmJSMaxImports <= MaxImports, "conservative");
+static_assert(AsmJSFirstDefFuncIndex < MaxFuncs, "conservative");
+
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_binary_h
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -105,19 +105,19 @@ ModuleGenerator::initAsmJS(Metadata* asm
 
     metadata_ = asmJSMetadata;
     MOZ_ASSERT(isAsmJS());
 
     // For asm.js, the Vectors in ModuleEnvironment are max-sized reservations
     // and will be initialized in a linear order via init* functions as the
     // module is generated.
 
-    MOZ_ASSERT(env_->sigs.length() == MaxSigs);
-    MOZ_ASSERT(env_->tables.length() == MaxTables);
-    MOZ_ASSERT(env_->asmJSSigToTableIndex.length() == MaxSigs);
+    MOZ_ASSERT(env_->sigs.length() == AsmJSMaxTypes);
+    MOZ_ASSERT(env_->tables.length() == AsmJSMaxTables);
+    MOZ_ASSERT(env_->asmJSSigToTableIndex.length() == AsmJSMaxTypes);
 
     return true;
 }
 
 bool
 ModuleGenerator::initWasm()
 {
     MOZ_ASSERT(!env_->isAsmJS());
@@ -1008,17 +1008,17 @@ ModuleGenerator::finishFuncDefs()
     return true;
 }
 
 bool
 ModuleGenerator::initSigTableLength(uint32_t sigIndex, uint32_t length)
 {
     MOZ_ASSERT(isAsmJS());
     MOZ_ASSERT(length != 0);
-    MOZ_ASSERT(length <= MaxTableElems);
+    MOZ_ASSERT(length <= MaxTableLength);
 
     MOZ_ASSERT(env_->asmJSSigToTableIndex[sigIndex] == 0);
     env_->asmJSSigToTableIndex[sigIndex] = numTables_;
 
     TableDesc& table = env_->tables[numTables_++];
     table.kind = TableKind::TypedFunction;
     table.limits.initial = length;
     table.limits.maximum = Some(length);
--- a/js/src/wasm/WasmTable.cpp
+++ b/js/src/wasm/WasmTable.cpp
@@ -145,17 +145,17 @@ Table::grow(uint32_t delta, JSContext* c
     // onMovingGrowTable does not fire when length == maximum.
     if (!delta)
         return length_;
 
     uint32_t oldLength = length_;
 
     CheckedInt<uint32_t> newLength = oldLength;
     newLength += delta;
-    if (!newLength.isValid())
+    if (!newLength.isValid() || newLength.value() > MaxTableLength)
         return -1;
 
     if (maximum_ && newLength.value() > maximum_.value())
         return -1;
 
     MOZ_ASSERT(movingGrowable());
 
     JSRuntime* rt = cx;  // Use JSRuntime's MallocProvider to avoid throwing.
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1521,43 +1521,12 @@ struct MemoryPatch
 
     void offsetBy(uint32_t delta) {
         offset += delta;
     }
 };
 
 WASM_DECLARE_POD_VECTOR(MemoryPatch, MemoryPatchVector)
 
-// Constants:
-
-static const unsigned NaN64GlobalDataOffset       = 0;
-static const unsigned NaN32GlobalDataOffset       = NaN64GlobalDataOffset + sizeof(double);
-static const unsigned InitialGlobalDataBytes      = NaN32GlobalDataOffset + sizeof(float);
-
-static const unsigned MaxSigs                     =        4 * 1024;
-static const unsigned MaxFuncs                    =      512 * 1024;
-static const unsigned MaxGlobals                  =        4 * 1024;
-static const unsigned MaxLocals                   =       64 * 1024;
-static const unsigned MaxImports                  =       64 * 1024;
-static const unsigned MaxExports                  =       64 * 1024;
-static const unsigned MaxTables                   =        4 * 1024;
-static const unsigned MaxTableElems               =     1024 * 1024;
-static const unsigned MaxDataSegments             =       64 * 1024;
-static const unsigned MaxElemSegments             =       64 * 1024;
-static const unsigned MaxArgsPerFunc              =        4 * 1024;
-static const unsigned MaxBrTableElems             = 4 * 1024 * 1024;
-
-// To be able to assign function indices during compilation while the number of
-// imports is still unknown, asm.js sets a maximum number of imports so it can
-// immediately start handing out function indices starting at the maximum + 1.
-// this means that there is a "hole" between the last import and the first
-// definition, but that's fine.
-
-static const unsigned AsmJSMaxImports             = 4 * 1024;
-static const unsigned AsmJSFirstDefFuncIndex      = AsmJSMaxImports + 1;
-
-static_assert(AsmJSMaxImports <= MaxImports, "conservative");
-static_assert(AsmJSFirstDefFuncIndex < MaxFuncs, "conservative");
-
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_types_h
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -696,16 +696,19 @@ wasm::ValidateFunctionBody(const ModuleE
     return f.iter().readFunctionEnd();
 }
 
 // Section macros.
 
 static bool
 DecodePreamble(Decoder& d)
 {
+    if (d.bytesRemain() > MaxModuleBytes)
+        return d.fail("module too big");
+
     uint32_t u32;
     if (!d.readFixedU32(&u32) || u32 != MagicNumber)
         return d.fail("failed to match magic number");
 
     if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
         return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
                       u32, EncodingVersion);
 
@@ -720,32 +723,32 @@ DecodeTypeSection(Decoder& d, ModuleEnvi
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     uint32_t numSigs;
     if (!d.readVarU32(&numSigs))
         return d.fail("expected number of signatures");
 
-    if (numSigs > MaxSigs)
+    if (numSigs > MaxTypes)
         return d.fail("too many signatures");
 
     if (!env->sigs.resize(numSigs))
         return false;
 
     for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
         uint32_t form;
         if (!d.readVarU32(&form) || form != uint32_t(TypeCode::Func))
             return d.fail("expected function form");
 
         uint32_t numArgs;
         if (!d.readVarU32(&numArgs))
             return d.fail("bad number of function args");
 
-        if (numArgs > MaxArgsPerFunc)
+        if (numArgs > MaxParams)
             return d.fail("too many arguments in signature");
 
         ValTypeVector args;
         if (!args.resize(numArgs))
             return false;
 
         for (uint32_t i = 0; i < numArgs; i++) {
             if (!DecodeValType(d, ModuleKind::Wasm, &args[i]))
@@ -780,16 +783,19 @@ DecodeTypeSection(Decoder& d, ModuleEnvi
 
 static UniqueChars
 DecodeName(Decoder& d)
 {
     uint32_t numBytes;
     if (!d.readVarU32(&numBytes))
         return nullptr;
 
+    if (numBytes > MaxStringBytes)
+        return nullptr;
+
     const uint8_t* bytes;
     if (!d.readBytes(numBytes, &bytes))
         return nullptr;
 
     UniqueChars name(js_pod_malloc<char>(numBytes + 1));
     if (!name)
         return nullptr;
 
@@ -850,17 +856,17 @@ DecodeTableLimits(Decoder& d, TableDescV
 
     if (elementType != uint32_t(TypeCode::AnyFunc))
         return d.fail("expected 'anyfunc' element type");
 
     Limits limits;
     if (!DecodeLimits(d, &limits))
         return false;
 
-    if (limits.initial > MaxTableElems)
+    if (limits.initial > MaxTableLength)
         return d.fail("too many table elements");
 
     if (tables->length())
         return d.fail("already have default table");
 
     return tables->emplaceBack(TableKind::AnyFunction, limits);
 }
 
@@ -954,16 +960,18 @@ DecodeImport(Decoder& d, ModuleEnvironme
 
     switch (importKind) {
       case DefinitionKind::Function: {
         uint32_t sigIndex;
         if (!DecodeSignatureIndex(d, env->sigs, &sigIndex))
             return false;
         if (!env->funcSigs.append(&env->sigs[sigIndex]))
             return false;
+        if (env->funcSigs.length() > MaxFuncs)
+            return d.fail("too many functions");
         break;
       }
       case DefinitionKind::Table: {
         if (!DecodeTableLimits(d, &env->tables))
             return false;
         env->tables.back().external = true;
         break;
       }
@@ -976,16 +984,18 @@ DecodeImport(Decoder& d, ModuleEnvironme
         ValType type;
         bool isMutable;
         if (!DecodeGlobalType(d, &type, &isMutable))
             return false;
         if (!GlobalIsJSCompatible(d, type, isMutable))
             return false;
         if (!env->globals.append(GlobalDesc(type, isMutable, env->globals.length())))
             return false;
+        if (env->globals.length() > MaxGlobals)
+            return d.fail("too many globals");
         break;
       }
       default:
         return d.fail("unsupported import kind");
     }
 
     return env->imports.emplaceBack(Move(moduleName), Move(funcName), importKind);
 }
@@ -1387,16 +1397,19 @@ DecodeElemSection(Decoder& d, ModuleEnvi
         InitExpr offset;
         if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &offset))
             return false;
 
         uint32_t numElems;
         if (!d.readVarU32(&numElems))
             return d.fail("expected segment size");
 
+        if (numElems > MaxTableLength)
+            return d.fail("too many table elements");
+
         Uint32Vector elemFuncIndices;
         if (!elemFuncIndices.resize(numElems))
             return false;
 
         for (uint32_t i = 0; i < numElems; i++) {
             if (!d.readVarU32(&elemFuncIndices[i]))
                 return d.fail("failed to read element function index");
             if (elemFuncIndices[i] >= env->numFuncs())
@@ -1453,16 +1466,19 @@ wasm::DecodeModuleEnvironment(Decoder& d
 
 static bool
 DecodeFunctionBody(Decoder& d, const ModuleEnvironment& env, uint32_t funcIndex)
 {
     uint32_t bodySize;
     if (!d.readVarU32(&bodySize))
         return d.fail("expected number of function body bytes");
 
+    if (bodySize > MaxFunctionBytes)
+        return d.fail("function body too big");
+
     if (d.bytesRemain() < bodySize)
         return d.fail("function body length too big");
 
     const uint8_t* bodyBegin = d.currentPosition();
 
     if (!ValidateFunctionBody(env, funcIndex, d))
         return false;
 
@@ -1570,33 +1586,41 @@ MaybeDecodeNameSectionBody(Decoder& d, M
     NameInBytecodeVector funcNames;
     if (!funcNames.resize(numFuncNames))
         return;
 
     for (uint32_t i = 0; i < numFuncNames; i++) {
         uint32_t numBytes;
         if (!d.readVarU32(&numBytes))
             return;
+        if (numBytes > MaxStringLength)
+            return;
 
         NameInBytecode name;
         name.offset = d.currentOffset();
         name.length = numBytes;
         funcNames[i] = name;
 
         if (!d.readBytes(numBytes))
             return;
 
         // Skip local names for a function.
         uint32_t numLocals;
         if (!d.readVarU32(&numLocals))
             return;
+        if (numLocals > MaxLocals)
+            return;
+
         for (uint32_t j = 0; j < numLocals; j++) {
             uint32_t numBytes;
             if (!d.readVarU32(&numBytes))
                 return;
+            if (numBytes > MaxStringLength)
+                return;
+
             if (!d.readBytes(numBytes))
                 return;
         }
     }
 
     env->funcNames = Move(funcNames);
 }