Ignore:
Timestamp:
Nov 8, 2019, 8:58:49 AM (6 years ago)
Author:
[email protected]
Message:

Add a stack overflow check in Yarr::ByteCompiler::emitDisjunction().
https://bugs.webkit.org/show_bug.cgi?id=203936
<rdar://problem/56624724>

Reviewed by Saam Barati.

JSTests:

This issue originally manifested as incorrect-exception-assertion-in-operationRegExpExecNonGlobalOrSticky.js
failing on iOS devices due to its smaller stack. We adapted the original test
here using $vm.callWithStackSize() to reproduce the issue on x86_64 though it
has a much larger physical stack to work with. This new test will crash while
stepping on the guard page beyond the end of the stack if the fix is not applied.

  • stress/stack-overflow-in-yarr-byteCompile.js: Added.

Source/JavaScriptCore:

Basically, any functions below Yarr::ByteCompiler::compile() that recurses need
to check if it's safe to recurse before doing so. This patch adds the stack
checks in Yarr::ByteCompiler::compile() because it is the entry point to this
sub-system, and Yarr::ByteCompiler::emitDisjunction() because it is the only
function that recurses. All other functions called below compile() are either
leaf functions or have shallow stack usage. Hence, their stack needs are covered
by the DefaultReservedZone, and they do not need stack checks.

This patch also does the following:

  1. Added $vm.callWithStackSize() which can be used to call a test function near the end of the physical stack. This enables is to simulate the smaller stack size of more resource constrained devices.

$vm.callWithStackSize() uses inline asm to adjust the stack pointer and
does the callback via the JIT probe trampoline.

  1. Added the --disableOptionsFreezingForTesting to the jsc shell to make it possible to disable freezing of JSC options. $vm.callWithStackSize() relies on this to modify the VM's stack limits.
  1. Removed the inline modifier on VM::updateStackLimits() so that we can call it from $vm.callWithStackSize() as well. It is not a performance critical function and is rarely called.
  1. Added a JSDollarVMHelper class that other parts of the system can declare as a friend. This gives $vm a backdoor into the private functions and fields of classes for its debugging work. In this patch, we're only using it to access VM::updateVMStackLimits().
  • jsc.cpp:

(CommandLine::parseArguments):

  • runtime/VM.cpp:

(JSC::VM::updateStackLimits):

  • runtime/VM.h:
  • tools/JSDollarVM.cpp:

(JSC::JSDollarVMHelper::JSDollarVMHelper):
(JSC::JSDollarVMHelper::vmStackStart):
(JSC::JSDollarVMHelper::vmStackLimit):
(JSC::JSDollarVMHelper::vmSoftStackLimit):
(JSC::JSDollarVMHelper::updateVMStackLimits):
(JSC::callWithStackSizeProbeFunction):
(JSC::functionCallWithStackSize):
(JSC::JSDollarVM::finishCreation):
(IGNORE_WARNINGS_BEGIN): Deleted.

  • yarr/YarrInterpreter.cpp:

(JSC::Yarr::ByteCompiler::compile):
(JSC::Yarr::ByteCompiler::emitDisjunction):
(JSC::Yarr::ByteCompiler::isSafeToRecurse):

Source/WTF:

  1. Add a StackCheck utility class so that we don't have to keep reinventing this every time we need to add stack checking somewhere.
  2. Rename some arguments and constants in StackBounds to be more descriptive of what they actually are.
  • WTF.xcodeproj/project.pbxproj:
  • wtf/CMakeLists.txt:
  • wtf/StackBounds.h:

(WTF::StackBounds::recursionLimit const):

  • wtf/StackCheck.h: Added.

(WTF::StackCheck::StackCheck):
(WTF::StackCheck::isSafeToRecurse):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/yarr/YarrInterpreter.cpp

    r248846 r252239  
    3535#include <wtf/CheckedArithmetic.h>
    3636#include <wtf/DataLog.h>
     37#include <wtf/StackCheck.h>
    3738#include <wtf/text/CString.h>
    3839#include <wtf/text/WTFString.h>
     
    16951696    std::unique_ptr<BytecodePattern> compile(BumpPointerAllocator* allocator, ConcurrentJSLock* lock, ErrorCode& errorCode)
    16961697    {
     1698        if (UNLIKELY(!isSafeToRecurse())) {
     1699            errorCode = ErrorCode::TooManyDisjunctions;
     1700            return nullptr;
     1701        }
     1702
    16971703        regexBegin(m_pattern.m_numSubpatterns, m_pattern.m_body->m_callFrameSize, m_pattern.m_body->m_alternatives[0]->onceThrough());
    16981704        if (auto error = emitDisjunction(m_pattern.m_body, 0, 0)) {
     
    20292035    Optional<ErrorCode> WARN_UNUSED_RETURN emitDisjunction(PatternDisjunction* disjunction, Checked<unsigned, RecordOverflow> inputCountAlreadyChecked, unsigned parenthesesInputCountAlreadyChecked)
    20302036    {
     2037        if (UNLIKELY(!isSafeToRecurse()))
     2038            return ErrorCode::TooManyDisjunctions;
     2039
    20312040        for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) {
    20322041            auto currentCountAlreadyChecked = inputCountAlreadyChecked;
     
    24062415
    24072416private:
     2417    inline bool isSafeToRecurse() { return m_stackCheck.isSafeToRecurse(); }
     2418
    24082419    YarrPattern& m_pattern;
    24092420    std::unique_ptr<ByteDisjunction> m_bodyDisjunction;
     2421    StackCheck m_stackCheck;
    24102422    unsigned m_currentAlternativeIndex { 0 };
    24112423    Vector<ParenthesesStackEntry> m_parenthesesStack;
Note: See TracChangeset for help on using the changeset viewer.