Bug 1053692 - Do not use optimized stub for spread call with many arguments. r=jandem, a=sledru
authorTooru Fujisawa <arai_a@mac.com>
Fri, 15 Aug 2014 22:02:25 +0900
changeset 209394 bf59c8cff7ad23de4185f17dd39766e15870475c
parent 209393 e7bb9c0cbb403cbbcfd4b42940b3df3c7fe17d04
child 209395 f64e991dc1091d9fe0f78f963bb69627c2bafac3
push id6654
push userryanvm@gmail.com
push dateSun, 17 Aug 2014 19:46:29 +0000
treeherdermozilla-aurora@bf59c8cff7ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, sledru
bugs1053692
milestone33.0a2
Bug 1053692 - Do not use optimized stub for spread call with many arguments. r=jandem, a=sledru
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
@@ -5603,16 +5603,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,