Title: [201207] trunk/Source/_javascript_Core
Revision
201207
Author
[email protected]
Date
2016-05-19 19:37:53 -0700 (Thu, 19 May 2016)

Log Message

[JSC] FTL can crash on stack overflow
https://bugs.webkit.org/show_bug.cgi?id=157881
rdar://problem/24665964

Patch by Benjamin Poulain <[email protected]> on 2016-05-19
Reviewed by Michael Saboff.

The VM's m_largestFTLStackSize was never set anywhere (updateFTLLargestStackSize()
was never called). We forgot to change that when implementing B3.

Even when it is set, we still have a problem on OSR Exit.
If the last frame is a FTL frame and it OSR Exits, the space required for
that frame becomes significantly larger. What happens is we crash in the OSR Exit
instead of the FTL frame (this is what happens in rdar://problem/24665964).

This patch changes the stack boundary checks in FTL to be the same as DFG:
we verify that we have enough space for the current optimized function but
also for the baseline version (including inlining) in case of exit.

* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::lower):
(JSC::FTL::DFG::LowerDFGToB3::didOverflowStack): Deleted.
* runtime/VM.cpp:
(JSC::VM::VM): Deleted.
(JSC::VM::updateStackLimit): Deleted.
(JSC::VM::updateFTLLargestStackSize): Deleted.
* runtime/VM.h:
(JSC::VM::addressOfFTLStackLimit): Deleted.

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (201206 => 201207)


--- trunk/Source/_javascript_Core/ChangeLog	2016-05-20 02:25:51 UTC (rev 201206)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-05-20 02:37:53 UTC (rev 201207)
@@ -1,3 +1,33 @@
+2016-05-19  Benjamin Poulain  <[email protected]>
+
+        [JSC] FTL can crash on stack overflow
+        https://bugs.webkit.org/show_bug.cgi?id=157881
+        rdar://problem/24665964
+
+        Reviewed by Michael Saboff.
+
+        The VM's m_largestFTLStackSize was never set anywhere (updateFTLLargestStackSize()
+        was never called). We forgot to change that when implementing B3.
+
+        Even when it is set, we still have a problem on OSR Exit.
+        If the last frame is a FTL frame and it OSR Exits, the space required for
+        that frame becomes significantly larger. What happens is we crash in the OSR Exit
+        instead of the FTL frame (this is what happens in rdar://problem/24665964).
+
+        This patch changes the stack boundary checks in FTL to be the same as DFG:
+        we verify that we have enough space for the current optimized function but
+        also for the baseline version (including inlining) in case of exit.
+
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::lower):
+        (JSC::FTL::DFG::LowerDFGToB3::didOverflowStack): Deleted.
+        * runtime/VM.cpp:
+        (JSC::VM::VM): Deleted.
+        (JSC::VM::updateStackLimit): Deleted.
+        (JSC::VM::updateFTLLargestStackSize): Deleted.
+        * runtime/VM.h:
+        (JSC::VM::addressOfFTLStackLimit): Deleted.
+
 2016-05-18  Filip Pizlo  <[email protected]>
 
         DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (201206 => 201207)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-05-20 02:25:51 UTC (rev 201206)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-05-20 02:37:53 UTC (rev 201207)
@@ -157,10 +157,7 @@
         m_out.setFrequency(1);
         
         m_prologue = m_out.newBlock();
-        LBasicBlock stackOverflow = m_out.newBlock();
         m_handleExceptions = m_out.newBlock();
-        
-        LBasicBlock checkArguments = m_out.newBlock();
 
         for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
             m_highBlock = m_graph.block(blockIndex);
@@ -173,7 +170,7 @@
         // Back to prologue frequency for any bocks that get sneakily created in the initialization code.
         m_out.setFrequency(1);
         
-        m_out.appendTo(m_prologue, stackOverflow);
+        m_out.appendTo(m_prologue, m_handleExceptions);
         m_out.initializeConstants(m_proc, m_prologue);
         createPhiVariables();
 
@@ -194,44 +191,54 @@
         m_proc.addFastConstant(m_tagMask->key());
         
         m_out.storePtr(m_out.constIntPtr(codeBlock()), addressFor(JSStack::CodeBlock));
-        
-        m_out.branch(
-            didOverflowStack(), rarely(stackOverflow), usually(checkArguments));
-        
-        m_out.appendTo(stackOverflow, m_handleExceptions);
-        m_out.call(m_out.voidType, m_out.operation(operationThrowStackOverflowError), m_callFrame, m_out.constIntPtr(codeBlock()));
-        m_out.patchpoint(Void)->setGenerator(
-            [=] (CCallHelpers& jit, const StackmapGenerationParams&) {
-                // We are terminal, so we can clobber everything. That's why we don't claim to
-                // clobber scratch.
+
+        // Stack Overflow Check.
+        unsigned exitFrameSize = m_graph.requiredRegisterCountForExit() * sizeof(Register);
+        MacroAssembler::AbsoluteAddress addressOfStackLimit(vm().addressOfStackLimit());
+        PatchpointValue* stackOverflowHandler = m_out.patchpoint(Void);
+        CallSiteIndex callSiteIndex = callSiteIndexForCodeOrigin(m_ftlState, CodeOrigin(0));
+        stackOverflowHandler->appendSomeRegister(m_callFrame);
+        stackOverflowHandler->clobber(RegisterSet::macroScratchRegisters());
+        stackOverflowHandler->numGPScratchRegisters = 1;
+        stackOverflowHandler->setGenerator(
+            [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
                 AllowMacroScratchRegisterUsage allowScratch(jit);
-                
-                jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer();
-                jit.move(CCallHelpers::TrustedImmPtr(jit.vm()), GPRInfo::argumentGPR0);
-                jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
-                CCallHelpers::Call call = jit.call();
-                jit.jumpToExceptionHandler();
+                GPRReg fp = params[0].gpr();
+                GPRReg scratch = params.gpScratch(0);
 
-                jit.addLinkTask(
-                    [=] (LinkBuffer& linkBuffer) {
-                        linkBuffer.link(call, FunctionPtr(lookupExceptionHandlerFromCallerFrame));
+                unsigned ftlFrameSize = params.proc().frameSize();
+
+                jit.addPtr(MacroAssembler::TrustedImm32(-std::max(exitFrameSize, ftlFrameSize)), fp, scratch);
+                MacroAssembler::Jump stackOverflow = jit.branchPtr(MacroAssembler::Above, addressOfStackLimit, scratch);
+
+                params.addLatePath([=] (CCallHelpers& jit) {
+                    AllowMacroScratchRegisterUsage allowScratch(jit);
+
+                    stackOverflow.link(&jit);
+                    jit.store32(
+                        MacroAssembler::TrustedImm32(callSiteIndex.bits()),
+                        CCallHelpers::tagFor(VirtualRegister(JSStack::ArgumentCount)));
+                    jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer();
+
+                    jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+                    jit.move(CCallHelpers::TrustedImmPtr(jit.codeBlock()), GPRInfo::argumentGPR1);
+                    CCallHelpers::Call throwCall = jit.call();
+
+                    jit.move(CCallHelpers::TrustedImmPtr(jit.vm()), GPRInfo::argumentGPR0);
+                    jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
+                    CCallHelpers::Call lookupExceptionHandlerCall = jit.call();
+                    jit.jumpToExceptionHandler();
+
+                    jit.addLinkTask(
+                        [=] (LinkBuffer& linkBuffer) {
+                            linkBuffer.link(throwCall, FunctionPtr(operationThrowStackOverflowError));
+                            linkBuffer.link(lookupExceptionHandlerCall, FunctionPtr(lookupExceptionHandlerFromCallerFrame));
                     });
+                });
             });
-        m_out.unreachable();
-        
-        m_out.appendTo(m_handleExceptions, checkArguments);
-        Box<CCallHelpers::Label> exceptionHandler = state->exceptionHandler;
-        m_out.patchpoint(Void)->setGenerator(
-            [=] (CCallHelpers& jit, const StackmapGenerationParams&) {
-                CCallHelpers::Jump jump = jit.jump();
-                jit.addLinkTask(
-                    [=] (LinkBuffer& linkBuffer) {
-                        linkBuffer.link(jump, linkBuffer.locationOf(*exceptionHandler));
-                    });
-            });
-        m_out.unreachable();
-        
-        m_out.appendTo(checkArguments, lowBlock(m_graph.block(0)));
+
+        LBasicBlock firstDFGBasicBlock = lowBlock(m_graph.block(0));
+        // Check Arguments.
         availabilityMap().clear();
         availabilityMap().m_locals = Operands<Availability>(codeBlock()->numParameters(), 0);
         for (unsigned i = codeBlock()->numParameters(); i--;) {
@@ -273,8 +280,20 @@
                 break;
             }
         }
-        m_out.jump(lowBlock(m_graph.block(0)));
-        
+        m_out.jump(firstDFGBasicBlock);
+
+        m_out.appendTo(m_handleExceptions, firstDFGBasicBlock);
+        Box<CCallHelpers::Label> exceptionHandler = state->exceptionHandler;
+        m_out.patchpoint(Void)->setGenerator(
+            [=] (CCallHelpers& jit, const StackmapGenerationParams&) {
+                CCallHelpers::Jump jump = jit.jump();
+                jit.addLinkTask(
+                    [=] (LinkBuffer& linkBuffer) {
+                        linkBuffer.link(jump, linkBuffer.locationOf(*exceptionHandler));
+                    });
+            });
+        m_out.unreachable();
+
         for (DFG::BasicBlock* block : preOrder)
             compileBlock(block);
 
@@ -7056,42 +7075,6 @@
             m_out.constInt32(0),
             m_out.address(constructor, m_heaps.RegExpConstructor_cachedResult_reified));
     }
-
-    LValue didOverflowStack()
-    {
-        // This does a very simple leaf function analysis. The invariant of FTL call
-        // frames is that the caller had already done enough of a stack check to
-        // prove that this call frame has enough stack to run, and also enough stack
-        // to make runtime calls. So, we only need to stack check when making calls
-        // to other JS functions. If we don't find such calls then we don't need to
-        // do any stack checks.
-        
-        for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
-            DFG::BasicBlock* block = m_graph.block(blockIndex);
-            if (!block)
-                continue;
-            
-            for (unsigned nodeIndex = block->size(); nodeIndex--;) {
-                Node* node = block->at(nodeIndex);
-                
-                switch (node->op()) {
-                case GetById:
-                case PutById:
-                case Call:
-                case Construct:
-                    return m_out.below(
-                        m_callFrame,
-                        m_out.loadPtr(
-                            m_out.absolute(vm().addressOfFTLStackLimit())));
-                    
-                default:
-                    break;
-                }
-            }
-        }
-        
-        return m_out.booleanFalse;
-    }
     
     struct ArgumentsLength {
         ArgumentsLength()

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (201206 => 201207)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2016-05-20 02:25:51 UTC (rev 201206)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2016-05-20 02:37:53 UTC (rev 201207)
@@ -188,10 +188,6 @@
 #if !ENABLE(JIT)
     , m_jsStackLimit(0)
 #endif
-#if ENABLE(FTL_JIT)
-    , m_ftlStackLimit(0)
-    , m_largestFTLStackSize(0)
-#endif
     , m_inDefineOwnProperty(false)
     , m_codeCache(std::make_unique<CodeCache>())
     , m_enabledProfiler(nullptr)
@@ -666,19 +662,9 @@
     if (m_stackPointerAtVMEntry) {
         ASSERT(wtfThreadData().stack().isGrowingDownward());
         char* startOfStack = reinterpret_cast<char*>(m_stackPointerAtVMEntry);
-#if ENABLE(FTL_JIT)
-        m_stackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize + m_largestFTLStackSize);
-        m_ftlStackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize + 2 * m_largestFTLStackSize);
-#else
         m_stackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize);
-#endif
     } else {
-#if ENABLE(FTL_JIT)
-        m_stackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize + m_largestFTLStackSize);
-        m_ftlStackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize + 2 * m_largestFTLStackSize);
-#else
         m_stackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize);
-#endif
     }
 
 #if PLATFORM(WIN)
@@ -687,16 +673,6 @@
 #endif
 }
 
-#if ENABLE(FTL_JIT)
-void VM::updateFTLLargestStackSize(size_t stackSize)
-{
-    if (stackSize > m_largestFTLStackSize) {
-        m_largestFTLStackSize = stackSize;
-        updateStackLimit();
-    }
-}
-#endif
-
 #if ENABLE(DFG_JIT)
 void VM::gatherConservativeRoots(ConservativeRoots& conservativeRoots)
 {

Modified: trunk/Source/_javascript_Core/runtime/VM.h (201206 => 201207)


--- trunk/Source/_javascript_Core/runtime/VM.h	2016-05-20 02:25:51 UTC (rev 201206)
+++ trunk/Source/_javascript_Core/runtime/VM.h	2016-05-20 02:37:53 UTC (rev 201207)
@@ -445,11 +445,6 @@
     size_t reservedZoneSize() const { return m_reservedZoneSize; }
     size_t updateReservedZoneSize(size_t reservedZoneSize);
 
-#if ENABLE(FTL_JIT)
-    void updateFTLLargestStackSize(size_t);
-    void** addressOfFTLStackLimit() { return &m_ftlStackLimit; }
-#endif
-
 #if !ENABLE(JIT)
     void* jsStackLimit() { return m_jsStackLimit; }
     void setJSStackLimit(void* limit) { m_jsStackLimit = limit; }
@@ -644,11 +639,7 @@
         void* m_stackLimit;
         void* m_jsStackLimit;
     };
-#if ENABLE(FTL_JIT)
-    void* m_ftlStackLimit;
-    size_t m_largestFTLStackSize;
 #endif
-#endif
     void* m_lastStackTop;
     Exception* m_exception { nullptr };
     Exception* m_lastException { nullptr };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to