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 327150 d01c0cd382843219b3ff12dc4a675d32b29809a7
parent 327149 d98d1548d5dd92e6cb5ad6e7d052e12526e59502
child 327151 ec3d424e593ebce4b99c1857cbf74350245c67b6
push id85116
push userlwagner@mozilla.com
push dateFri, 23 Dec 2016 19:28:24 +0000
treeherdermozilla-inbound@ec3d424e593e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1324008
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);
 }