Changeset 203142 in webkit


Ignore:
Timestamp:
Jul 12, 2016 5:19:15 PM (8 years ago)
Author:
mark.lam@apple.com
Message:

We should use different stack limits for stack checks from JS and host code.
https://bugs.webkit.org/show_bug.cgi?id=159442
<rdar://problem/26889188>

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

We have 2 stack reservedZoneSizes:

  1. Options::softReservedZoneSize()
  2. Options::reservedZoneSize()

Respectively, there are used to define 2 stack limits based on these reserved
zone sizes:

  1. VM::m_softStackLimit
  2. VM::m_stackLimit

Options::reservedZoneSize() is the amount of the stack space that JSC guarantees
to the VM and client host code for it's use. Host code that has well known
stack usage characteristics (i.e. doesn't call arbitrary code) may do stack
checks against the VM::m_stackLimit limit (which is computed using
Options::reservedZoneSize()).

Options::softReservedZoneSize() is a more conservative amount of reserved stack
space. This is used to compute the VM::m_softStackLimit limit. Any code that
is difficult to have its stack usage characterized (i.e. may call arbitrary code)
may need more stack space for its work. Hence, these should do stack checks
against the VM::m_softStackLimit limit.

JS code and host code that may call into JS code falls into the category of code
that may call arbitrary code. Hence, they should do stack checks against the
VM::m_softStackLimit limit.

Accordingly, the VM now provides 2 recursion check functions:

  1. VM::isSafeToRecurseSoft() will do a stack check against VM::m_softStackLimit. In addition, for C Loop builds, VM::isSafeToRecurseSoft() will also check the CLoopStack against VM::m_cloopStackLimit.
  1. VM::isSafeToRecurse() will do a stack check against VM::m_stackLimit.

Also added a promise-infinite-recursion-should-not-crash.js test.

  • bytecompiler/BytecodeGenerator.h:

(JSC::BytecodeGenerator::emitNodeInTailPosition):
(JSC::BytecodeGenerator::emitNodeInConditionContext):

  • interpreter/CLoopStack.cpp:

(JSC::CLoopStack::grow):

  • interpreter/CLoopStack.h:

(JSC::CLoopStack::size):

  • interpreter/CLoopStackInlines.h:

(JSC::CLoopStack::ensureCapacityFor):
(JSC::CLoopStack::isSafeToRecurse):
(JSC::CLoopStack::topOfFrameFor):

  • interpreter/CachedCall.h:

(JSC::CachedCall::CachedCall):

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::execute):
(JSC::Interpreter::executeCall):
(JSC::Interpreter::executeConstruct):

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL):

  • parser/Parser.cpp:
  • runtime/Options.h:
  • runtime/ProxyObject.cpp:

(JSC::performProxyGet):
(JSC::ProxyObject::performInternalMethodGetOwnProperty):
(JSC::ProxyObject::performHasProperty):
(JSC::ProxyObject::getOwnPropertySlotCommon):
(JSC::ProxyObject::performPut):
(JSC::performProxyCall):
(JSC::performProxyConstruct):
(JSC::ProxyObject::performDelete):
(JSC::ProxyObject::performPreventExtensions):
(JSC::ProxyObject::performIsExtensible):
(JSC::ProxyObject::performDefineOwnProperty):
(JSC::ProxyObject::performGetOwnPropertyNames):
(JSC::ProxyObject::performSetPrototype):
(JSC::ProxyObject::performGetPrototype):

  • runtime/RegExp.cpp:

(JSC::RegExp::finishCreation):
(JSC::RegExp::compile):
(JSC::RegExp::compileMatchOnly):

  • runtime/StringRecursionChecker.h:

(JSC::StringRecursionChecker::performCheck):

  • runtime/VM.cpp:

(JSC::VM::setStackPointerAtVMEntry):
(JSC::VM::updateSoftReservedZoneSize):
(JSC::preCommitStackMemory):
(JSC::VM::updateStackLimits):
(JSC::VM::updateStackLimit): Deleted.

  • runtime/VM.h:

(JSC::VM::stackLimit):
(JSC::VM::softStackLimit):
(JSC::VM::addressOfSoftStackLimit):
(JSC::VM::setCLoopStackLimit):
(JSC::VM::isSafeToRecurse):
(JSC::VM::lastStackTop):
(JSC::VM::setException):

  • runtime/VMInlines.h:

(JSC::VM::ensureStackCapacityFor):
(JSC::VM::isSafeToRecurseSoft):
(JSC::VM::shouldTriggerTermination):

  • tests/stress/promise-infinite-recursion-should-not-crash.js: Added.

(testPromise):
(promiseFunc):

  • yarr/YarrPattern.cpp:

Tools:

In http://trac.webkit.org/r203067, we limited the amount of stack that tests will
run with to keep stack overflow tests sane. Turns out, we also need to teach the
LayoutTestRelay to pass env vars over to the iOS simulator. This is needed in
order to keep the js/regress-139548.html test happy with this patch.

Also fixed up run_webkit_tests.py to explicitly pass an int size value for the
JSC_maxPerThreadStackUsage option. Otherwise, it will pass a float value.

  • LayoutTestRelay/LayoutTestRelay/LTRelayController.m:

(-[LTRelayController _environmentVariables]):

  • Scripts/webkitpy/layout_tests/run_webkit_tests.py:

(main):

LayoutTests:

  • js/regress-141098-expected.txt:
  • js/script-tests/regress-141098.js:

(testEval):
(probeAndRecurse):

  • Gave all the test constants names.
  • Tweaked the constants to allow the test to run in the least amount of time, and also to behave consistently across all test configurations.
  • Re-enable eager tests now that the test should finish quickly.
Location:
trunk
Files:
1 added
23 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r203137 r203142  
     12016-07-12  Mark Lam  <mark.lam@apple.com>
     2
     3        We should use different stack limits for stack checks from JS and host code.
     4        https://bugs.webkit.org/show_bug.cgi?id=159442
     5        <rdar://problem/26889188>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        * js/regress-141098-expected.txt:
     10        * js/script-tests/regress-141098.js:
     11        (testEval):
     12        (probeAndRecurse):
     13        - Gave all the test constants names.
     14        - Tweaked the constants to allow the test to run in the least amount of time, and
     15          also to behave consistently across all test configurations.
     16        - Re-enable eager tests now that the test should finish quickly.
     17
    1182016-07-12  Dean Jackson  <dino@apple.com>
    219
  • trunk/LayoutTests/js/regress-141098-expected.txt

    r200117 r203142  
    55
    66PASS Initial setup
     7Starting 1st probeAndRecurse
    78PASS Exception: RangeError: Maximum call stack size exceeded.
     9Starting 2nd probeAndRecurse
    810PASS Exception: RangeError: Maximum call stack size exceeded.
    911PASS successfullyParsed is true
  • trunk/LayoutTests/js/script-tests/regress-141098.js

    r200117 r203142  
    1 //@ noEagerNoNoLLIntTestsRunLayoutTest
     1//@ noNoLLIntRunLayoutTest
    22
    33description("Regression test for https://webkit.org/b/141098. Make sure eval() properly handles running out of stack space. This test should run without crashing.");
     
    66// if run in run-jsc-stress-tests with the eager settings.
    77
     8// countStart, countIncrement, and numberOfFramesToBackoffFromStackOverflowPoint were
     9// empirically determined to be the values that will cause StackOverflowErrors to be
     10// thrown where the tests expects them. If stack frame layouts change sufficiently (e.g.
     11// due to JIT changes) such that this test starts failing (due to text output
     12// differences), then these values will need to be rebased.
     13// Under no circumstance should this test ever crash.
     14let countStart = 2;
     15let countIncrement = 8;
     16let numberOfFramesToBackoffFromStackOverflowPoint = 50;
     17
     18// backoffEverything is chosen to be -1 because a negative number will never be
     19// decremented to 0, and hence, will cause probeAndRecurse() to return out of every
     20// frame instead of retrying the eval test.
     21let backoffEverything = -1;
     22
    823var lastEvalString = "";
    924
     
    1126{
    1227    var result;
    13     var count = 1;
     28    var count = countStart;
    1429
    1530    if (!maxIterations)
    1631        var result = eval(lastEvalString);
    1732    else {
    18         for (var iter = 0; iter < maxIterations; count *= 4, iter++) {
     33        for (var iter = 0; iter < maxIterations; count *= countIncrement, iter++) {
    1934            var evalString = "\"dummy\".valueOf(";
    2035
     
    3651}
    3752
    38 function probeAndRecurse(depth, reuseEvalString)
     53function probeAndRecurse(reuseEvalString)
    3954{
    40     var result;
     55    var framesToBackOffFromStackOverflowPoint;
    4156
    4257    // Probe stack depth
    4358    try {
    44         result = probeAndRecurse(depth+1, reuseEvalString);
     59        remainingFramesToBackOff = probeAndRecurse(reuseEvalString);
    4560
    46         if (!result) {
     61        if (!remainingFramesToBackOff) {
     62            // We've backed off a number of frames. Now retry the eval test to see if we
     63            // still overflow.
    4764            try {
    4865                testEval(1);
    4966            } catch (e) {
    50                 return -49;
     67                // Yikes. We still overflow. Back off some more.
     68                return numberOfFramesToBackoffFromStackOverflowPoint;
    5169            }
    5270        } else
    53             return result + 1
     71            return remainingFramesToBackOff - 1
    5472    } catch (e) {
    5573        // We exceeded stack space, now return up the stack until we can execute a simple eval.
    5674        // Then run an eval test to exceed stack.
    57         return -49;
     75        return numberOfFramesToBackoffFromStackOverflowPoint;
    5876    }
    5977
     
    6482    }
    6583
    66     return 1;
     84    return backoffEverything;
    6785}
    6886
    69 // Because this test intentionlly exhausts the stack, we call testPassed() to ensure
     87// Because this test intentionally exhausts the stack, we call testPassed() to ensure
    7088// everything we need in that path has been compiled and is available.  Otherwise we
    7189// might properly handle an out of stack, but run out of stack calling testPassed().
    7290testPassed("Initial setup");
    7391
    74 var depth = probeAndRecurse(0, false);
     92debug("Starting 1st probeAndRecurse");
     93probeAndRecurse(false);
    7594
    7695// Tier up the eval'ed code.
     
    8099    testEval(0);
    81100
    82 probeAndRecurse(0, true);
     101debug("Starting 2nd probeAndRecurse");
     102probeAndRecurse(true);
  • trunk/Source/JavaScriptCore/ChangeLog

    r203134 r203142  
     12016-07-12  Mark Lam  <mark.lam@apple.com>
     2
     3        We should use different stack limits for stack checks from JS and host code.
     4        https://bugs.webkit.org/show_bug.cgi?id=159442
     5        <rdar://problem/26889188>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        We have 2 stack reservedZoneSizes:
     10        1. Options::softReservedZoneSize()
     11        2. Options::reservedZoneSize()
     12
     13        Respectively, there are used to define 2 stack limits based on these reserved
     14        zone sizes:
     15        1. VM::m_softStackLimit
     16        2. VM::m_stackLimit
     17
     18        Options::reservedZoneSize() is the amount of the stack space that JSC guarantees
     19        to the VM and client host code for it's use.  Host code that has well known
     20        stack usage characteristics (i.e. doesn't call arbitrary code) may do stack
     21        checks against the VM::m_stackLimit limit (which is computed using
     22        Options::reservedZoneSize()).
     23
     24        Options::softReservedZoneSize() is a more conservative amount of reserved stack
     25        space.  This is used to compute the VM::m_softStackLimit limit.  Any code that
     26        is difficult to have its stack usage characterized (i.e. may call arbitrary code)
     27        may need more stack space for its work.  Hence, these should do stack checks
     28        against the VM::m_softStackLimit limit.
     29
     30        JS code and host code that may call into JS code falls into the category of code
     31        that may call arbitrary code.  Hence, they should do stack checks against the
     32        VM::m_softStackLimit limit.
     33
     34        Accordingly, the VM now provides 2 recursion check functions:
     35
     36        1. VM::isSafeToRecurseSoft() will do a stack check against VM::m_softStackLimit.
     37           In addition, for C Loop builds, VM::isSafeToRecurseSoft() will also
     38           check the CLoopStack against VM::m_cloopStackLimit.
     39
     40        2. VM::isSafeToRecurse() will do a stack check against VM::m_stackLimit.
     41
     42        Also added a promise-infinite-recursion-should-not-crash.js test.
     43
     44        * bytecompiler/BytecodeGenerator.h:
     45        (JSC::BytecodeGenerator::emitNodeInTailPosition):
     46        (JSC::BytecodeGenerator::emitNodeInConditionContext):
     47        * interpreter/CLoopStack.cpp:
     48        (JSC::CLoopStack::grow):
     49        * interpreter/CLoopStack.h:
     50        (JSC::CLoopStack::size):
     51        * interpreter/CLoopStackInlines.h:
     52        (JSC::CLoopStack::ensureCapacityFor):
     53        (JSC::CLoopStack::isSafeToRecurse):
     54        (JSC::CLoopStack::topOfFrameFor):
     55        * interpreter/CachedCall.h:
     56        (JSC::CachedCall::CachedCall):
     57        * interpreter/Interpreter.cpp:
     58        (JSC::Interpreter::execute):
     59        (JSC::Interpreter::executeCall):
     60        (JSC::Interpreter::executeConstruct):
     61        * llint/LLIntSlowPaths.cpp:
     62        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
     63        * parser/Parser.cpp:
     64        * runtime/Options.h:
     65        * runtime/ProxyObject.cpp:
     66        (JSC::performProxyGet):
     67        (JSC::ProxyObject::performInternalMethodGetOwnProperty):
     68        (JSC::ProxyObject::performHasProperty):
     69        (JSC::ProxyObject::getOwnPropertySlotCommon):
     70        (JSC::ProxyObject::performPut):
     71        (JSC::performProxyCall):
     72        (JSC::performProxyConstruct):
     73        (JSC::ProxyObject::performDelete):
     74        (JSC::ProxyObject::performPreventExtensions):
     75        (JSC::ProxyObject::performIsExtensible):
     76        (JSC::ProxyObject::performDefineOwnProperty):
     77        (JSC::ProxyObject::performGetOwnPropertyNames):
     78        (JSC::ProxyObject::performSetPrototype):
     79        (JSC::ProxyObject::performGetPrototype):
     80        * runtime/RegExp.cpp:
     81        (JSC::RegExp::finishCreation):
     82        (JSC::RegExp::compile):
     83        (JSC::RegExp::compileMatchOnly):
     84        * runtime/StringRecursionChecker.h:
     85        (JSC::StringRecursionChecker::performCheck):
     86        * runtime/VM.cpp:
     87        (JSC::VM::setStackPointerAtVMEntry):
     88        (JSC::VM::updateSoftReservedZoneSize):
     89        (JSC::preCommitStackMemory):
     90        (JSC::VM::updateStackLimits):
     91        (JSC::VM::updateStackLimit): Deleted.
     92        * runtime/VM.h:
     93        (JSC::VM::stackLimit):
     94        (JSC::VM::softStackLimit):
     95        (JSC::VM::addressOfSoftStackLimit):
     96        (JSC::VM::setCLoopStackLimit):
     97        (JSC::VM::isSafeToRecurse):
     98        (JSC::VM::lastStackTop):
     99        (JSC::VM::setException):
     100        * runtime/VMInlines.h:
     101        (JSC::VM::ensureStackCapacityFor):
     102        (JSC::VM::isSafeToRecurseSoft):
     103        (JSC::VM::shouldTriggerTermination):
     104        * tests/stress/promise-infinite-recursion-should-not-crash.js: Added.
     105        (testPromise):
     106        (promiseFunc):
     107        * yarr/YarrPattern.cpp:
     108
    11092016-07-12  Per Arne Vollan  <pvollan@apple.com>
    2110
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r203006 r203142  
    385385            // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary.
    386386            ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount());
    387             if (!m_vm->isSafeToRecurse()) {
     387            if (UNLIKELY(!m_vm->isSafeToRecurse())) {
    388388                emitThrowExpressionTooDeepException();
    389389                return;
     
    412412            // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary.
    413413            ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount());
    414             if (!m_vm->isSafeToRecurse())
     414            if (UNLIKELY(!m_vm->isSafeToRecurse()))
    415415                return emitThrowExpressionTooDeepException();
    416416            return n->emitBytecode(*this, dst);
     
    429429        void emitNodeInConditionContext(ExpressionNode* n, Label* trueTarget, Label* falseTarget, FallThroughMode fallThroughMode)
    430430        {
    431             if (!m_vm->isSafeToRecurse()) {
     431            if (UNLIKELY(!m_vm->isSafeToRecurse())) {
    432432                emitThrowExpressionTooDeepException();
    433433                return;
  • trunk/Source/JavaScriptCore/interpreter/CLoopStack.cpp

    r203130 r203142  
    8989
    9090    // Compute the chunk size of additional memory to commit, and see if we
    91     // have it is still within our budget. If not, we'll fail to grow and
     91    // have it still within our budget. If not, we'll fail to grow and
    9292    // return false.
    9393    ptrdiff_t delta = reinterpret_cast<char*>(m_commitTop) - reinterpret_cast<char*>(newTopOfStackWithReservedZone);
  • trunk/Source/JavaScriptCore/interpreter/CLoopStack.h

    r203130 r203142  
    6969
    7070        void setSoftReservedZoneSize(size_t);
     71        bool isSafeToRecurse() const;
    7172
    7273        inline Register* topOfStack();
  • trunk/Source/JavaScriptCore/interpreter/CLoopStackInlines.h

    r203130 r203142  
    4343}
    4444
     45bool CLoopStack::isSafeToRecurse() const
     46{
     47    void* reservationLimit = reinterpret_cast<int8_t*>(reservationTop() + m_reservedZoneSizeInRegisters);
     48    return !m_topCallFrame || (m_topCallFrame->topOfFrame() > reservationLimit);
     49}
     50
    4551inline Register* CLoopStack::topOfFrameFor(CallFrame* frame)
    4652{
  • trunk/Source/JavaScriptCore/interpreter/CachedCall.h

    r163960 r203142  
    11/*
    2  * Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2009, 2013, 2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3434#include "ProtoCallFrame.h"
    3535#include "VMEntryScope.h"
     36#include "VMInlines.h"
    3637
    3738namespace JSC {
     
    4546        {
    4647            ASSERT(!function->isHostFunctionNonInline());
    47             if (callFrame->vm().isSafeToRecurse()) {
     48            if (UNLIKELY(callFrame->vm().isSafeToRecurseSoft())) {
    4849                m_arguments.resize(argumentCount);
    4950                m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, &m_protoCallFrame, function, argumentCount + 1, function->scope(), m_arguments.data());
  • trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp

    r203081 r203142  
    829829        return jsNull();
    830830
    831     if (!vm.isSafeToRecurse())
     831    if (UNLIKELY(!vm.isSafeToRecurseSoft()))
    832832        return checkedReturn(throwStackOverflowError(callFrame));
    833833
     
    989989
    990990    VMEntryScope entryScope(vm, globalObject);
    991     if (!vm.isSafeToRecurse())
     991    if (UNLIKELY(!vm.isSafeToRecurseSoft()))
    992992        return checkedReturn(throwStackOverflowError(callFrame));
    993993
     
    10511051
    10521052    VMEntryScope entryScope(vm, globalObject);
    1053     if (!vm.isSafeToRecurse())
     1053    if (UNLIKELY(!vm.isSafeToRecurseSoft()))
    10541054        return checkedReturn(throwStackOverflowError(callFrame));
    10551055
     
    11481148
    11491149    VMEntryScope entryScope(vm, scope->globalObject());
    1150     if (!vm.isSafeToRecurse())
     1150    if (UNLIKELY(!vm.isSafeToRecurseSoft()))
    11511151        return checkedReturn(throwStackOverflowError(callFrame));       
    11521152
     
    12491249
    12501250    VMEntryScope entryScope(vm, scope->globalObject());
    1251     if (!vm.isSafeToRecurse())
     1251    if (UNLIKELY(!vm.isSafeToRecurseSoft()))
    12521252        return checkedReturn(throwStackOverflowError(callFrame));
    12531253
  • trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp

    r203130 r203142  
    483483    dataLogF("Num vars = %u.\n", exec->codeBlock()->m_numVars);
    484484
    485 #if ENABLE(JIT)
    486     dataLogF("Current end is at %p.\n", exec->vm().softStackLimit());
    487 #else
    488     dataLogF("Current end is at %p.\n", exec->vm().cloopStackLimit());
     485    dataLogF("Current OS stack end is at %p.\n", exec->vm().softStackLimit());
     486#if !ENABLE(JIT)
     487    dataLogF("Current C Loop stack end is at %p.\n", exec->vm().cloopStackLimit());
    489488#endif
    490489
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r202865 r203142  
    5656#define consumeOrFailWithFlags(tokenType, flags, ...) do { if (!consume(tokenType, flags)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0)
    5757#define matchOrFail(tokenType, ...) do { if (!match(tokenType)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0)
    58 #define failIfStackOverflow() do { if (!canRecurse()) failWithStackOverflow(); } while (0)
     58#define failIfStackOverflow() do { if (UNLIKELY(!canRecurse())) failWithStackOverflow(); } while (0)
    5959#define semanticFail(...) do { internalFailWithMessage(false, __VA_ARGS__); } while (0)
    6060#define semanticFailIfTrue(cond, ...) do { if (cond) internalFailWithMessage(false, __VA_ARGS__); } while (0)
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r203130 r203142  
    114114    \
    115115    v(unsigned, maxPerThreadStackUsage, 4 * MB, Normal, "Max allowed stack usage by the VM") \
    116     v(unsigned, softReservedZoneSize, 128 * KB, Normal, "The amount of stack JSC usually reserves for host code.") \
    117     v(unsigned, reservedZoneSize, 64 * KB, Normal, "This is the amount of stack JSC guarantees for client and VM code.") \
     116    v(unsigned, softReservedZoneSize, 128 * KB, Normal, "A buffer greater than reservedZoneSize that reserves space for stringifying exceptions.") \
     117    v(unsigned, reservedZoneSize, 64 * KB, Normal, "The amount of stack space we guarantee to our clients (and to interal VM code that does not call out to clients).") \
    118118    \
    119119    v(bool, crashIfCantAllocateJITMemory, false, Normal, nullptr) \
  • trunk/Source/JavaScriptCore/runtime/ProxyObject.cpp

    r202124 r203142  
    3434#include "SlotVisitorInlines.h"
    3535#include "StructureInlines.h"
     36#include "VMInlines.h"
    3637
    3738// Note that this file is compile with -fno-optimize-sibling-calls because we rely on the machine stack
     
    99100{
    100101    VM& vm = exec->vm();
    101     if (UNLIKELY(!vm.isSafeToRecurse())) {
     102    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    102103        throwStackOverflowError(exec);
    103104        return JSValue();
     
    168169{
    169170    VM& vm = exec->vm();
    170     if (UNLIKELY(!vm.isSafeToRecurse())) {
     171    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    171172        throwStackOverflowError(exec);
    172173        return false;
     
    274275{
    275276    VM& vm = exec->vm();
    276     if (UNLIKELY(!vm.isSafeToRecurse())) {
     277    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    277278        throwStackOverflowError(exec);
    278279        return false;
     
    339340bool ProxyObject::getOwnPropertySlotCommon(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
    340341{
    341     if (UNLIKELY(!exec->vm().isSafeToRecurse())) {
     342    if (UNLIKELY(!exec->vm().isSafeToRecurseSoft())) {
    342343        throwStackOverflowError(exec);
    343344        return false;
     
    377378{
    378379    VM& vm = exec->vm();
    379     if (UNLIKELY(!vm.isSafeToRecurse())) {
     380    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    380381        throwStackOverflowError(exec);
    381382        return false;
     
    469470{
    470471    VM& vm = exec->vm();
    471     if (UNLIKELY(!vm.isSafeToRecurse())) {
     472    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    472473        throwStackOverflowError(exec);
    473474        return JSValue::encode(JSValue());
     
    518519{
    519520    VM& vm = exec->vm();
    520     if (UNLIKELY(!vm.isSafeToRecurse())) {
     521    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    521522        throwStackOverflowError(exec);
    522523        return JSValue::encode(JSValue());
     
    573574{
    574575    VM& vm = exec->vm();
    575     if (UNLIKELY(!vm.isSafeToRecurse())) {
     576    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    576577        throwStackOverflowError(exec);
    577578        return false;
     
    649650{
    650651    VM& vm = exec->vm();
    651     if (UNLIKELY(!vm.isSafeToRecurse())) {
     652    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    652653        throwStackOverflowError(exec);
    653654        return false;
     
    701702{
    702703    VM& vm = exec->vm();
    703     if (UNLIKELY(!vm.isSafeToRecurse())) {
     704    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    704705        throwStackOverflowError(exec);
    705706        return false;
     
    759760{
    760761    VM& vm = exec->vm();
    761     if (UNLIKELY(!vm.isSafeToRecurse())) {
     762    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    762763        throwStackOverflowError(exec);
    763764        return false;
     
    856857{
    857858    VM& vm = exec->vm();
    858     if (UNLIKELY(!vm.isSafeToRecurse())) {
     859    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    859860        throwStackOverflowError(exec);
    860861        return;
     
    10051006
    10061007    VM& vm = exec->vm();
    1007     if (UNLIKELY(!vm.isSafeToRecurse())) {
     1008    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    10081009        throwStackOverflowError(exec);
    10091010        return false;
     
    10691070{
    10701071    VM& vm = exec->vm();
    1071     if (UNLIKELY(!vm.isSafeToRecurse())) {
     1072    if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
    10721073        throwStackOverflowError(exec);
    10731074        return JSValue();
  • trunk/Source/JavaScriptCore/runtime/RegExp.cpp

    r203130 r203142  
    223223{
    224224    Base::finishCreation(vm);
    225     Yarr::YarrPattern pattern(m_patternString, m_flags, &m_constructionError, vm.softStackLimit());
     225    Yarr::YarrPattern pattern(m_patternString, m_flags, &m_constructionError, vm.stackLimit());
    226226    if (m_constructionError)
    227227        m_state = ParseError;
     
    265265    ConcurrentJITLocker locker(m_lock);
    266266   
    267     Yarr::YarrPattern pattern(m_patternString, m_flags, &m_constructionError, vm->softStackLimit());
     267    Yarr::YarrPattern pattern(m_patternString, m_flags, &m_constructionError, vm->stackLimit());
    268268    if (m_constructionError) {
    269269        RELEASE_ASSERT_NOT_REACHED();
     
    318318    ConcurrentJITLocker locker(m_lock);
    319319   
    320     Yarr::YarrPattern pattern(m_patternString, m_flags, &m_constructionError, vm->softStackLimit());
     320    Yarr::YarrPattern pattern(m_patternString, m_flags, &m_constructionError, vm->stackLimit());
    321321    if (m_constructionError) {
    322322        RELEASE_ASSERT_NOT_REACHED();
  • trunk/Source/JavaScriptCore/runtime/StringRecursionChecker.h

    r184447 r203142  
    11/*
    2  *  Copyright (C) 2011 Apple Inc. All rights reserved.
     2 *  Copyright (C) 2011, 2016 Apple Inc. All rights reserved.
    33 *
    44 *  This library is free software; you can redistribute it and/or
     
    2222
    2323#include "Interpreter.h"
     24#include "VMInlines.h"
    2425#include <wtf/StackStats.h>
    2526#include <wtf/WTFThreadData.h>
     
    5152{
    5253    VM& vm = m_exec->vm();
    53     if (!vm.isSafeToRecurse())
     54    if (UNLIKELY(!vm.isSafeToRecurseSoft()))
    5455        return throwStackOverflowError();
    5556
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r203130 r203142  
    611611{
    612612    m_stackPointerAtVMEntry = sp;
    613     updateStackLimit();
     613    updateStackLimits();
    614614}
    615615
     
    622622#endif
    623623
    624     updateStackLimit();
     624    updateStackLimits();
    625625
    626626    return oldSoftReservedZoneSize;
     
    652652#endif
    653653
    654 inline void VM::updateStackLimit()
     654inline void VM::updateStackLimits()
    655655{
    656656#if PLATFORM(WIN)
     
    658658#endif
    659659
     660    size_t reservedZoneSize = Options::reservedZoneSize();
    660661    if (m_stackPointerAtVMEntry) {
    661662        ASSERT(wtfThreadData().stack().isGrowingDownward());
    662663        char* startOfStack = reinterpret_cast<char*>(m_stackPointerAtVMEntry);
    663664        m_softStackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_currentSoftReservedZoneSize);
     665        m_stackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), reservedZoneSize);
    664666    } else {
    665667        m_softStackLimit = wtfThreadData().stack().recursionLimit(m_currentSoftReservedZoneSize);
     668        m_stackLimit = wtfThreadData().stack().recursionLimit(reservedZoneSize);
    666669    }
    667670
    668671#if PLATFORM(WIN)
     672    // We only need to precommit stack memory dictated by the VM::m_softStackLimit limit.
     673    // This is because VM::m_softStackLimit applies to stack usage by LLINT asm or JIT
     674    // generated code which can allocate stack space that the C++ compiler does not know
     675    // about. As such, we have to precommit that stack memory manually.
     676    //
     677    // In contrast, we do not need to worry about VM::m_stackLimit because that limit is
     678    // used exclusively by C++ code, and the C++ compiler will automatically commit the
     679    // needed stack pages.
    669680    if (lastSoftStackLimit != m_softStackLimit)
    670681        preCommitStackMemory(m_softStackLimit);
  • trunk/Source/JavaScriptCore/runtime/VM.h

    r203130 r203142  
    466466    inline bool ensureStackCapacityFor(Register* newTopOfStack);
    467467
     468    void* stackLimit() { return m_stackLimit; }
    468469    void* softStackLimit() { return m_softStackLimit; }
    469470    void** addressOfSoftStackLimit() { return &m_softStackLimit; }
     
    473474#endif
    474475
    475     bool isSafeToRecurse(size_t neededStackInBytes = 0) const
    476     {
    477         ASSERT(wtfThreadData().stack().isGrowingDownward());
    478         int8_t* curr = reinterpret_cast<int8_t*>(&curr);
    479         int8_t* limit = reinterpret_cast<int8_t*>(m_softStackLimit);
    480         return curr >= limit && static_cast<size_t>(curr - limit) >= neededStackInBytes;
     476    inline bool isSafeToRecurseSoft() const;
     477    bool isSafeToRecurse() const
     478    {
     479        return isSafeToRecurse(m_stackLimit);
    481480    }
    482481
     
    627626    void createNativeThunk();
    628627
    629     void updateStackLimit();
     628    void updateStackLimits();
     629
     630    bool isSafeToRecurse(void* stackLimit) const
     631    {
     632        ASSERT(wtfThreadData().stack().isGrowingDownward());
     633        void* curr = reinterpret_cast<void*>(&curr);
     634        return curr >= stackLimit;
     635    }
    630636
    631637    void setException(Exception* exception)
     
    650656    void* m_stackPointerAtVMEntry;
    651657    size_t m_currentSoftReservedZoneSize;
     658    void* m_stackLimit { nullptr };
    652659    void* m_softStackLimit { nullptr };
    653660#if !ENABLE(JIT)
  • trunk/Source/JavaScriptCore/runtime/VMInlines.h

    r203130 r203142  
    4848}
    4949
     50bool VM::isSafeToRecurseSoft() const
     51{
     52    bool safe = isSafeToRecurse(m_softStackLimit);
     53#if !ENABLE(JIT)
     54    safe = safe && interpreter->cloopStack().isSafeToRecurse();
     55#endif
     56    return safe;
     57}
     58
    5059bool VM::shouldTriggerTermination(ExecState* exec)
    5160{
  • trunk/Source/JavaScriptCore/yarr/YarrPattern.cpp

    r202490 r203142  
    581581    bool setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition, unsigned& newCallFrameSize) WARN_UNUSED_RETURN
    582582    {
    583         if (!isSafeToRecurse())
     583        if (UNLIKELY(!isSafeToRecurse()))
    584584            return false;
    585585
     
    683683    bool setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition, unsigned& callFrameSize) WARN_UNUSED_RETURN
    684684    {
    685         if (!isSafeToRecurse())
     685        if (UNLIKELY(!isSafeToRecurse()))
    686686            return false;
    687687
  • trunk/Tools/ChangeLog

    r203141 r203142  
     12016-07-12  Mark Lam  <mark.lam@apple.com>
     2
     3        We should use different stack limits for stack checks from JS and host code.
     4        https://bugs.webkit.org/show_bug.cgi?id=159442
     5        <rdar://problem/26889188>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        In http://trac.webkit.org/r203067, we limited the amount of stack that tests will
     10        run with to keep stack overflow tests sane.  Turns out, we also need to teach the
     11        LayoutTestRelay to pass env vars over to the iOS simulator.  This is needed in
     12        order to keep the js/regress-139548.html test happy with this patch.
     13
     14        Also fixed up run_webkit_tests.py to explicitly pass an int size value for the
     15        JSC_maxPerThreadStackUsage option.  Otherwise, it will pass a float value.
     16
     17        * LayoutTestRelay/LayoutTestRelay/LTRelayController.m:
     18        (-[LTRelayController _environmentVariables]):
     19        * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
     20        (main):
     21
    1222016-07-12  Filip Pizlo  <fpizlo@apple.com>
    223
  • trunk/Tools/LayoutTestRelay/LayoutTestRelay/LTRelayController.m

    r202743 r203142  
    2929#import "LTPipeRelay.h"
    3030#import <AppKit/AppKit.h>
     31#import <crt_externs.h>
    3132
    3233@interface LTRelayController ()
     
    194195        }
    195196
     197        for (char** envp = *_NSGetEnviron(); *envp; envp++) {
     198            const char* env = *envp;
     199            if (!strncmp("JSC_", env, 4) || !strncmp("__XPC_JSC_", env, 10)) {
     200                const char* equal = strchr(env, '=');
     201                if (!equal) {
     202                    NSLog(@"Missing '=' in env var '%s'", env);
     203                    continue;
     204                }
     205
     206                static const size_t maxKeyLength = 256;
     207                size_t keyLength = equal - env;
     208                if (keyLength >= maxKeyLength) {
     209                    NSLog(@"Env var '%s' is too long", env);
     210                    continue;
     211                }
     212
     213                char key[maxKeyLength];
     214                strncpy(key, env, keyLength);
     215                key[keyLength] = '\0';
     216                const char* value = equal + 1;
     217
     218                NSString *nsKey = [NSString stringWithUTF8String:key];
     219                NSString *nsValue = [NSString stringWithUTF8String:value];
     220                [dictionary setObject:nsValue forKey:nsKey];
     221            }
     222        }
     223
    196224        environmentVariables = [dictionary copy];
    197225    });
  • trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py

    r203067 r203142  
    7676    try:
    7777        # Force all tests to use a smaller stack so that stack overflow tests can run faster.
    78         stackSizeInBytes = 1.5 * 1024 * 1024
     78        stackSizeInBytes = int(1.5 * 1024 * 1024)
    7979        options.additional_env_var.append('JSC_maxPerThreadStackUsage=' + str(stackSizeInBytes))
    8080        options.additional_env_var.append('__XPC_JSC_maxPerThreadStackUsage=' + str(stackSizeInBytes))
Note: See TracChangeset for help on using the changeset viewer.