Bug 1053692 - Do not use optimized stub for spread call with many arguments. r=jandem
authorTooru Fujisawa <arai_a@mac.com>
Fri, 15 Aug 2014 22:02:25 +0900
changeset 199936 0f0be3fb8a150d689165a0d02e32d2d385448b03
parent 199935 62d9d374cd0fcdd6b58409ed4fa7b6f4c348699f
child 199937 2d7cc0aef9f9733ba3c4cfc3fd5e7e451e45e695
push id47763
push usergkwong@mozilla.com
push dateSat, 16 Aug 2014 07:13:34 +0000
treeherdermozilla-inbound@0f0be3fb8a15 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1053692
milestone34.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 1053692 - Do not use optimized stub for spread call with many arguments. r=jandem
js/src/jit-test/tests/basic/spread-call-near-maxarg.js
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/spread-call-near-maxarg.js
@@ -0,0 +1,73 @@
+var config = getBuildConfiguration();
+
+// FIXME: ASAN and debug builds run this too slowly for now.
+if (!config.debug && !config.asan) {
+    let longArray = [];
+    longArray.length = getMaxArgs() - 1;
+    let shortArray = [];
+    let a;
+
+    let f = function() {
+    };
+
+    // Call_Scripted
+    //   Optimized stub is used after some calls.
+    a = shortArray;
+    for (let i = 0; i < 4; i++) {
+        if (i == 3) {
+            a = longArray;
+        }
+        try {
+            f(...a);
+        } catch (e) {
+            assertEq(e.message, "too much recursion");
+        }
+    }
+
+    // Call_Scripted (constructing)
+    a = shortArray;
+    for (let i = 0; i < 4; i++) {
+        if (i == 3) {
+            a = longArray;
+        }
+        try {
+            new f(...a);
+        } catch (e) {
+            assertEq(e.message, "too much recursion");
+        }
+    }
+
+    // Call_Native
+    a = shortArray;
+    for (let i = 0; i < 4; i++) {
+        if (i == 3) {
+            a = longArray;
+        }
+        try {
+            Math.max(...a);
+        } catch (e) {
+            assertEq(e.message, "too much recursion");
+        }
+    }
+
+    // Call_Native (constructing)
+    a = shortArray;
+    for (let i = 0; i < 4; i++) {
+        if (i == 3) {
+            a = longArray;
+        }
+        try {
+            new Date(...a);
+        } catch (e) {
+            assertEq(e.message, "too much recursion");
+        }
+    }
+
+    // No optimized stub for eval.
+    a = longArray;
+    try {
+        eval(...a);
+    } catch (e) {
+        assertEq(e.message, "too much recursion");
+    }
+}
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -8516,18 +8516,22 @@ ICCallStubCompiler::pushCallArguments(Ma
 
 void
 ICCallStubCompiler::guardSpreadCall(MacroAssembler &masm, Register argcReg, Label *failure)
 {
     masm.unboxObject(Address(BaselineStackReg, ICStackValueOffset), argcReg);
     masm.loadPtr(Address(argcReg, JSObject::offsetOfElements()), argcReg);
     masm.load32(Address(argcReg, ObjectElements::offsetOfLength()), argcReg);
 
-    // Ensure actual argc <= ARGS_LENGTH_MAX
-    masm.branch32(Assembler::Above, argcReg, Imm32(ARGS_LENGTH_MAX), failure);
+    // Limit actual argc to something reasonable (huge number of arguments can
+    // blow the stack limit).
+    static_assert(ICCall_Scripted::MAX_ARGS_SPREAD_LENGTH <= ARGS_LENGTH_MAX,
+                  "maximum arguments length for optimized stub should be <= ARGS_LENGTH_MAX");
+    masm.branch32(Assembler::Above, argcReg, Imm32(ICCall_Scripted::MAX_ARGS_SPREAD_LENGTH),
+                  failure);
 }
 
 void
 ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler &masm, GeneralRegisterSet regs,
                                             Register argcReg)
 {
     // Push arguments
     Register startReg = regs.takeAny();
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -5601,16 +5601,21 @@ class ICCall_Fallback : public ICMonitor
             return stub;
         }
     };
 };
 
 class ICCall_Scripted : public ICMonitoredStub
 {
     friend class ICStubSpace;
+  public:
+    // The maximum number of inlineable spread call arguments. Keep this small
+    // to avoid controllable stack overflows by attackers passing large arrays
+    // to spread call. This value is shared with ICCall_Native.
+    static const uint32_t MAX_ARGS_SPREAD_LENGTH = 16;
 
   protected:
     HeapPtrScript calleeScript_;
     HeapPtrObject templateObject_;
     uint32_t pcOffset_;
 
     ICCall_Scripted(JitCode *stubCode, ICStub *firstMonitorStub,
                     HandleScript calleeScript, HandleObject templateObject,