Changeset 207861 in webkit


Ignore:
Timestamp:
Oct 25, 2016 6:15:49 PM (7 years ago)
Author:
mark.lam@apple.com
Message:

String.prototype.replace() should throw an OutOfMemoryError when using too much memory.
https://bugs.webkit.org/show_bug.cgi?id=163996
<rdar://problem/28263117>

Reviewed by Geoffrey Garen.

JSTests:

  • stress/string-prototype-replace-should-throw-out-of-memory-error-when-using-too-much-memory.js: Added.

Source/JavaScriptCore:

String.prototype.replace() uses a Vector internally for bookkeeping work.
Currently, if this vector gets too big, we just crash on allocation failure.
While this is correct behavior, it is not too friendly.

We now detect the imminent failure, and throw a OutOfMemoryError instead.

  • runtime/StringPrototype.cpp:

(JSC::removeUsingRegExpSearch):
(JSC::replaceUsingRegExpSearch):
(JSC::operationStringProtoFuncReplaceRegExpEmptyStr):
(JSC::stringProtoFuncReplaceUsingRegExp):

Source/WTF:

  • wtf/Vector.h:

(WTF::minCapacity>::tryConstructAndAppend):
(WTF::minCapacity>::tryConstructAndAppendSlowCase):

  • Added try versions of constructAndAppend() so that we can handle the failure to allocate more gracefully.
Location:
trunk
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r207849 r207861  
     12016-10-25  Mark Lam  <mark.lam@apple.com>
     2
     3        String.prototype.replace() should throw an OutOfMemoryError when using too much memory.
     4        https://bugs.webkit.org/show_bug.cgi?id=163996
     5        <rdar://problem/28263117>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        * stress/string-prototype-replace-should-throw-out-of-memory-error-when-using-too-much-memory.js: Added.
     10
    1112016-10-25  Mark Lam  <mark.lam@apple.com>
    212
  • trunk/Source/JavaScriptCore/ChangeLog

    r207859 r207861  
     12016-10-25  Mark Lam  <mark.lam@apple.com>
     2
     3        String.prototype.replace() should throw an OutOfMemoryError when using too much memory.
     4        https://bugs.webkit.org/show_bug.cgi?id=163996
     5        <rdar://problem/28263117>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        String.prototype.replace() uses a Vector internally for bookkeeping work.
     10        Currently, if this vector gets too big, we just crash on allocation failure.
     11        While this is correct behavior, it is not too friendly.
     12
     13        We now detect the imminent failure, and throw a OutOfMemoryError instead.
     14
     15        * runtime/StringPrototype.cpp:
     16        (JSC::removeUsingRegExpSearch):
     17        (JSC::replaceUsingRegExpSearch):
     18        (JSC::operationStringProtoFuncReplaceRegExpEmptyStr):
     19        (JSC::stringProtoFuncReplaceUsingRegExp):
     20
    1212016-10-25  Mark Lam  <mark.lam@apple.com>
    222
  • trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp

    r207851 r207861  
    434434}
    435435
     436#define OUT_OF_MEMORY(exec__, scope__) \
     437    do { \
     438        throwOutOfMemoryError(exec__, scope__); \
     439        return encodedJSValue(); \
     440    } while (false)
     441
    436442static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, const String& source, RegExp* regExp)
    437443{
     444    auto scope = DECLARE_THROW_SCOPE(vm);
    438445    SuperSamplerScope superSamplerScope(false);
    439446   
     
    450457            break;
    451458
    452         if (lastIndex < result.start)
    453             sourceRanges.constructAndAppend(lastIndex, result.start - lastIndex);
    454 
     459        if (lastIndex < result.start) {
     460            if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
     461                OUT_OF_MEMORY(exec, scope);
     462        }
    455463        lastIndex = result.end;
    456464        startPosition = lastIndex;
     
    467475        return JSValue::encode(string);
    468476
    469     if (static_cast<unsigned>(lastIndex) < sourceLen)
    470         sourceRanges.constructAndAppend(lastIndex, sourceLen - lastIndex);
    471 
     477    if (static_cast<unsigned>(lastIndex) < sourceLen) {
     478        if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, sourceLen - lastIndex)))
     479            OUT_OF_MEMORY(exec, scope);
     480    }
     481    scope.release();
    472482    return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
    473483}
     
    491501        RETURN_IF_EXCEPTION(scope, encodedJSValue());
    492502
    493         if (callType == CallType::None && !replacementString.length())
     503        if (callType == CallType::None && !replacementString.length()) {
     504            scope.release();
    494505            return removeUsingRegExpSearch(vm, exec, string, source, regExp);
     506        }
    495507    }
    496508
     
    519531                    break;
    520532
    521                 sourceRanges.constructAndAppend(lastIndex, result.start - lastIndex);
     533                if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
     534                    OUT_OF_MEMORY(exec, scope);
    522535
    523536                unsigned i = 0;
     
    557570                    break;
    558571
    559                 sourceRanges.constructAndAppend(lastIndex, result.start - lastIndex);
     572                if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
     573                    OUT_OF_MEMORY(exec, scope);
    560574
    561575                unsigned i = 0;
     
    597611
    598612            if (callType != CallType::None) {
    599                 sourceRanges.constructAndAppend(lastIndex, result.start - lastIndex);
     613                if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
     614                    OUT_OF_MEMORY(exec, scope);
    600615
    601616                MarkedArgumentBuffer args;
     
    621636                int replLen = replacementString.length();
    622637                if (lastIndex < result.start || replLen) {
    623                     sourceRanges.constructAndAppend(lastIndex, result.start - lastIndex);
     638                    if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
     639                        OUT_OF_MEMORY(exec, scope);
    624640
    625641                    if (replLen)
     
    645661        return JSValue::encode(string);
    646662
    647     if (static_cast<unsigned>(lastIndex) < sourceLen)
    648         sourceRanges.constructAndAppend(lastIndex, sourceLen - lastIndex);
    649 
     663    if (static_cast<unsigned>(lastIndex) < sourceLen) {
     664        if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, sourceLen - lastIndex)))
     665            OUT_OF_MEMORY(exec, scope);
     666    }
    650667    return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
    651668}
     
    663680        searchValue->setLastIndex(exec, 0);
    664681        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     682        scope.release();
    665683        return removeUsingRegExpSearch(vm, exec, thisValue, thisValue->value(exec), regExp);
    666684    }
     
    668686    CallData callData;
    669687    String replacementString = emptyString();
     688    scope.release();
    670689    return replaceUsingRegExpSearch(
    671690        vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
     
    696715    }
    697716
     717    scope.release();
    698718    return replaceUsingRegExpSearch(
    699719        vm, exec, string, searchValue, callData, callType, replacementString, replaceValue);
     
    833853        return JSValue::encode(jsUndefined());
    834854
     855    scope.release();
    835856    return replaceUsingRegExpSearch(exec->vm(), exec, string, searchValue, exec->argument(1));
    836857}
  • trunk/Source/WTF/ChangeLog

    r207819 r207861  
     12016-10-25  Mark Lam  <mark.lam@apple.com>
     2
     3        String.prototype.replace() should throw an OutOfMemoryError when using too much memory.
     4        https://bugs.webkit.org/show_bug.cgi?id=163996
     5        <rdar://problem/28263117>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        * wtf/Vector.h:
     10        (WTF::minCapacity>::tryConstructAndAppend):
     11        (WTF::minCapacity>::tryConstructAndAppendSlowCase):
     12        - Added try versions of constructAndAppend() so that we can handle the failure
     13          to allocate more gracefully.
     14
    1152016-10-25  Konstantin Tokarev  <annulen@yandex.ru>
    216
  • trunk/Source/WTF/wtf/Vector.h

    r201314 r207861  
    723723    template<typename U> void append(U&&);
    724724    template<typename... Args> void constructAndAppend(Args&&...);
     725    template<typename... Args> bool tryConstructAndAppend(Args&&...);
    725726
    726727    void uncheckedAppend(ValueType&& value) { uncheckedAppend<ValueType>(std::forward<ValueType>(value)); }
     
    786787    template<typename U> void appendSlowCase(U&&);
    787788    template<typename... Args> void constructAndAppendSlowCase(Args&&...);
     789    template<typename... Args> bool tryConstructAndAppendSlowCase(Args&&...);
    788790
    789791    void asanSetInitialBufferSizeTo(size_t);
     
    12261228}
    12271229
     1230template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity> template<typename... Args>
     1231ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity>::tryConstructAndAppend(Args&&... args)
     1232{
     1233    if (size() != capacity()) {
     1234        asanBufferSizeWillChangeTo(m_size + 1);
     1235        new (NotNull, end()) T(std::forward<Args>(args)...);
     1236        ++m_size;
     1237        return true;
     1238    }
     1239   
     1240    return tryConstructAndAppendSlowCase(std::forward<Args>(args)...);
     1241}
     1242
    12281243template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity> template<typename U>
    12291244void Vector<T, inlineCapacity, OverflowHandler, minCapacity>::appendSlowCase(U&& value)
     
    12511266    new (NotNull, end()) T(std::forward<Args>(args)...);
    12521267    ++m_size;
     1268}
     1269
     1270template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity> template<typename... Args>
     1271bool Vector<T, inlineCapacity, OverflowHandler, minCapacity>::tryConstructAndAppendSlowCase(Args&&... args)
     1272{
     1273    ASSERT(size() == capacity());
     1274   
     1275    if (UNLIKELY(!tryExpandCapacity(size() + 1)))
     1276        return false;
     1277    ASSERT(begin());
     1278   
     1279    asanBufferSizeWillChangeTo(m_size + 1);
     1280    new (NotNull, end()) T(std::forward<Args>(args)...);
     1281    ++m_size;
     1282    return true;
    12531283}
    12541284
Note: See TracChangeset for help on using the changeset viewer.