Title: [237577] trunk
Revision
237577
Author
mark....@apple.com
Date
2018-10-29 19:58:51 -0700 (Mon, 29 Oct 2018)

Log Message

Correctly detect string overflow when using the 'Function' constructor.
https://bugs.webkit.org/show_bug.cgi?id=184883
<rdar://problem/36320331>

Reviewed by Saam Barati.

JSTests:

I've verified that this passes on 32-bit as well.

* slowMicrobenchmarks/function-constructor-with-huge-strings.js: Added.

Source/bmalloc:

* bmalloc/Allocator.cpp:
(bmalloc::Allocator::reallocate):
(bmalloc::Allocator::tryReallocate):
(bmalloc::Allocator::reallocateImpl):
* bmalloc/Allocator.h:
* bmalloc/Cache.h:
(bmalloc::Cache::tryReallocate):
* bmalloc/DebugHeap.cpp:
(bmalloc::DebugHeap::realloc):
* bmalloc/DebugHeap.h:
* bmalloc/bmalloc.h:
(bmalloc::api::tryRealloc):

Source/_javascript_Core:

Added StringBuilder::hasOverflowed() checks, and throwing OutOfMemoryErrors if
we detect an overflow.

* runtime/FunctionConstructor.cpp:
(JSC::constructFunctionSkippingEvalEnabledCheck):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::encode):
(JSC::decode):
* runtime/JSONObject.cpp:
(JSC::Stringifier::stringify):
(JSC::Stringifier::appendStringifiedValue):

Source/WTF:

1. Enhanced StringBuilder so that it supports 2 modes of behavior:
   a. StringBuilder::OverflowHandler::CrashOnOverflow.
   b. StringBuilder::OverflowHandler::RecordOverflow.

   CrashOnOverflow will crash the moment an overflow or failure to allocate
   memory is detected.

   RecordOverflow will make StringBuilder::hasOverflowed() return true when an
   overflow or failure to allocate memory is detected.  However, if the user
   invokes toString(), toStringPreserveCapacity(), toAtomicString(), length(),
   capacity(), isEmpty(), characters8(), or characters16(), then the StringBuilder
   will crash regardless because these methods can only meaningfully do their
   work and return a result if and only if the builder has not overflowed.

   By default, the StringBuilder crashes on overflow (the old behavior) unless it
   is explicitly constructed with the StringBuilder::OverflowHandler::RecordOverflow
   enum.

   Design note:

   The StringBuilder can only be put in 1 of the 2 modes at the time of
   construction.  This means that we could have implemented it as a template
   that is parameterized on an OverflowHandler class (like CheckedArithmetic)
   instead of using a runtime check in the ConditionalCrashOnOverflow handler.

   However, I was not able to get clang to export the explicitly instantiated
   instances (despite the methods having being decorated with WTF_EXPORT_PRIVATE).
   Since the StringBuilder is a transient object (not stored for a long time on
   the heap), and the runtime check of the boolean is not that costly compared
   to other work that StringBuilder routinely does (e.g. memcpy), using the
   new ConditionalCrashOnOverflow (which does a runtime check to determine to
   crash or not on overflow) is fine.

   When clang is able to export explicitly instantiated template methods, we can
   templatize StringBuilder and do away with ConditionalCrashOnOverflow.
   See https://bugs.webkit.org/show_bug.cgi?id=191050.

To support the above, we also did:

2. Enhanced all StringBuilder append and buffer allocation methods to be able to
   fail without crashing.  This also meant that ...

3. Added tryReallocate() support to StringImpl.  tryRealloc() was added to
   FastMalloc, and bmalloc (and related peers) to enable this.

4. Added ConditionalCrashOnOverflow, which can choose at runtime whether to behave
   like CrashOnOverflow or RecordOverflow.

* wtf/CheckedArithmetic.h:
(WTF::ConditionalCrashOnOverflow::overflowed):
(WTF::ConditionalCrashOnOverflow::shouldCrashOnOverflow const):
(WTF::ConditionalCrashOnOverflow::setShouldCrashOnOverflow):
(WTF::ConditionalCrashOnOverflow::hasOverflowed const):
(WTF::ConditionalCrashOnOverflow::clearOverflow):
(WTF::ConditionalCrashOnOverflow::crash):
(WTF::RecordOverflow::overflowed):
(WTF::Checked::unsafeGet const):
* wtf/FastMalloc.cpp:
(WTF::tryFastRealloc):
* wtf/FastMalloc.h:
* wtf/text/StringBuilder.cpp:
(WTF::expandedCapacity):
(WTF::StringBuilder::reifyString const):
(WTF::StringBuilder::resize):
(WTF::StringBuilder::allocateBuffer):
(WTF::StringBuilder::allocateBufferUpConvert):
(WTF::StringBuilder::reallocateBuffer<LChar>):
(WTF::StringBuilder::reallocateBuffer<UChar>):
(WTF::StringBuilder::reserveCapacity):
(WTF::StringBuilder::appendUninitialized):
(WTF::StringBuilder::appendUninitializedSlow):
(WTF::StringBuilder::append):
(WTF::StringBuilder::canShrink const):
(WTF::StringBuilder::shrinkToFit):
* wtf/text/StringBuilder.h:
(WTF::StringBuilder::StringBuilder):
(WTF::StringBuilder::didOverflow):
(WTF::StringBuilder::hasOverflowed const):
(WTF::StringBuilder::crashesOnOverflow const):
(WTF::StringBuilder::append):
(WTF::StringBuilder::toString):
(WTF::StringBuilder::toStringPreserveCapacity const):
(WTF::StringBuilder::toAtomicString const):
(WTF::StringBuilder::length const):
(WTF::StringBuilder::capacity const):
(WTF::StringBuilder::operator[] const):
(WTF::StringBuilder::swap):
(WTF::StringBuilder::getBufferCharacters<UChar>):
* wtf/text/StringBuilderJSON.cpp:
(WTF::StringBuilder::appendQuotedJSONString):
* wtf/text/StringImpl.cpp:
(WTF::StringImpl::reallocateInternal):
(WTF::StringImpl::reallocate):
(WTF::StringImpl::tryReallocate):
* wtf/text/StringImpl.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (237576 => 237577)


--- trunk/JSTests/ChangeLog	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/JSTests/ChangeLog	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,3 +1,15 @@
+2018-10-29  Mark Lam  <mark....@apple.com>
+
+        Correctly detect string overflow when using the 'Function' constructor.
+        https://bugs.webkit.org/show_bug.cgi?id=184883
+        <rdar://problem/36320331>
+
+        Reviewed by Saam Barati.
+
+        I've verified that this passes on 32-bit as well.
+
+        * slowMicrobenchmarks/function-constructor-with-huge-strings.js: Added.
+
 2018-10-29  Tadeu Zagallo  <tzaga...@apple.com>
 
         Add support for GetStack FlushedDouble

Added: trunk/JSTests/slowMicrobenchmarks/function-constructor-with-huge-strings.js (0 => 237577)


--- trunk/JSTests/slowMicrobenchmarks/function-constructor-with-huge-strings.js	                        (rev 0)
+++ trunk/JSTests/slowMicrobenchmarks/function-constructor-with-huge-strings.js	2018-10-30 02:58:51 UTC (rev 237577)
@@ -0,0 +1,19 @@
+var hugeString = "x";
+for (i = 0; i < 25; ++i) {
+    hugeString += hugeString;
+}
+
+var exception;
+var weird = '';
+try {
+    var f = new Function(hugeString, hugeString, hugeString, hugeString, hugeString, hugeString, hugeString,
+      hugeString, hugeString, hugeString, hugeString, hugeString, hugeString, hugeString,
+      hugeString, hugeString, hugeString, hugeString, hugeString, hugeString, hugeString,
+      () => 42,
+      "return 42;");
+} catch (e) {
+    exception = e;
+}
+
+if (exception != "Error: Out of memory")
+    throw "FAIL";

Modified: trunk/Source/_javascript_Core/ChangeLog (237576 => 237577)


--- trunk/Source/_javascript_Core/ChangeLog	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,3 +1,23 @@
+2018-10-29  Mark Lam  <mark....@apple.com>
+
+        Correctly detect string overflow when using the 'Function' constructor.
+        https://bugs.webkit.org/show_bug.cgi?id=184883
+        <rdar://problem/36320331>
+
+        Reviewed by Saam Barati.
+
+        Added StringBuilder::hasOverflowed() checks, and throwing OutOfMemoryErrors if
+        we detect an overflow.
+
+        * runtime/FunctionConstructor.cpp:
+        (JSC::constructFunctionSkippingEvalEnabledCheck):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::encode):
+        (JSC::decode):
+        * runtime/JSONObject.cpp:
+        (JSC::Stringifier::stringify):
+        (JSC::Stringifier::appendStringifiedValue):
+
 2018-10-29  Tadeu Zagallo  <tzaga...@apple.com>
 
         Unreviewed, fix JSC on arm64e after r237547

Modified: trunk/Source/_javascript_Core/runtime/FunctionConstructor.cpp (237576 => 237577)


--- trunk/Source/_javascript_Core/runtime/FunctionConstructor.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/_javascript_Core/runtime/FunctionConstructor.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -123,21 +123,25 @@
         RETURN_IF_EXCEPTION(scope, nullptr);
         program = makeString("{", prefix, functionName.string(), "() {\n", body, "\n}}");
     } else {
-        StringBuilder builder;
+        StringBuilder builder(StringBuilder::OverflowHandler::RecordOverflow);
         builder.append('{');
         builder.append(prefix);
         builder.append(functionName.string());
         builder.append('(');
-        StringBuilder parameterBuilder;
+        StringBuilder parameterBuilder(StringBuilder::OverflowHandler::RecordOverflow);
         auto viewWithString = args.at(0).toString(exec)->viewWithUnderlyingString(exec);
         RETURN_IF_EXCEPTION(scope, nullptr);
         parameterBuilder.append(viewWithString.view);
-        for (size_t i = 1; i < args.size() - 1; i++) {
+        for (size_t i = 1; !parameterBuilder.hasOverflowed() && i < args.size() - 1; i++) {
             parameterBuilder.appendLiteral(", ");
             auto viewWithString = args.at(i).toString(exec)->viewWithUnderlyingString(exec);
             RETURN_IF_EXCEPTION(scope, nullptr);
             parameterBuilder.append(viewWithString.view);
         }
+        if (parameterBuilder.hasOverflowed()) {
+            throwOutOfMemoryError(exec, scope);
+            return nullptr;
+        }
         auto body = args.at(args.size() - 1).toWTFString(exec);
         RETURN_IF_EXCEPTION(scope, nullptr);
 
@@ -164,6 +168,10 @@
         RETURN_IF_EXCEPTION(scope, nullptr);
         builder.append(body);
         builder.appendLiteral("\n}}");
+        if (builder.hasOverflowed()) {
+            throwOutOfMemoryError(exec, scope);
+            return nullptr;
+        }
         program = builder.toString();
     }
 

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp (237576 => 237577)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1999-2002 Harri Porten (por...@kde.org)
  *  Copyright (C) 2001 Peter Kelly (p...@post.com)
- *  Copyright (C) 2003-2017 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003-2018 Apple Inc. All rights reserved.
  *  Copyright (C) 2007 Cameron Zwarich (cwzwar...@uwaterloo.ca)
  *  Copyright (C) 2007 Maks Orlovich
  *
@@ -87,7 +87,7 @@
         return JSC::throwException(exec, scope, createURIError(exec, "String contained an illegal UTF-16 sequence."_s));
     };
 
-    StringBuilder builder;
+    StringBuilder builder(StringBuilder::OverflowHandler::RecordOverflow);
     builder.reserveCapacity(length);
 
     // 4. Repeat
@@ -151,6 +151,8 @@
         }
     }
 
+    if (UNLIKELY(builder.hasOverflowed()))
+        return throwOutOfMemoryError(exec, scope);
     return jsString(exec, builder.toString());
 }
 
@@ -170,7 +172,7 @@
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    StringBuilder builder;
+    StringBuilder builder(StringBuilder::OverflowHandler::RecordOverflow);
     int k = 0;
     UChar u = 0;
     while (k < length) {
@@ -229,6 +231,8 @@
         k++;
         builder.append(c);
     }
+    if (UNLIKELY(builder.hasOverflowed()))
+        return throwOutOfMemoryError(exec, scope);
     RELEASE_AND_RETURN(scope, jsString(&vm, builder.toString()));
 }
 

Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (237576 => 237577)


--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -273,7 +273,7 @@
         object->putDirect(vm, vm.propertyNames->emptyIdentifier, value);
     }
 
-    StringBuilder result;
+    StringBuilder result(StringBuilder::OverflowHandler::RecordOverflow);
     Holder root(Holder::RootHolder, object);
     auto stringifyResult = appendStringifiedValue(result, value, root, emptyPropertyName);
     EXCEPTION_ASSERT(!scope.exception() || (stringifyResult != StringifySucceeded));
@@ -358,10 +358,12 @@
     if (value.isString()) {
         const String& string = asString(value)->value(m_exec);
         RETURN_IF_EXCEPTION(scope, StringifyFailed);
-        if (builder.appendQuotedJSONString(string))
-            return StringifySucceeded;
-        throwOutOfMemoryError(m_exec, scope);
-        return StringifyFailed;
+        builder.appendQuotedJSONString(string);
+        if (UNLIKELY(builder.hasOverflowed())) {
+            throwOutOfMemoryError(m_exec, scope);
+            return StringifyFailed;
+        }
+        return StringifySucceeded;
     }
 
     if (value.isNumber()) {

Modified: trunk/Source/WTF/ChangeLog (237576 => 237577)


--- trunk/Source/WTF/ChangeLog	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/ChangeLog	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,3 +1,107 @@
+2018-10-29  Mark Lam  <mark....@apple.com>
+
+        Correctly detect string overflow when using the 'Function' constructor.
+        https://bugs.webkit.org/show_bug.cgi?id=184883
+        <rdar://problem/36320331>
+
+        Reviewed by Saam Barati.
+
+        1. Enhanced StringBuilder so that it supports 2 modes of behavior:
+           a. StringBuilder::OverflowHandler::CrashOnOverflow.
+           b. StringBuilder::OverflowHandler::RecordOverflow.
+
+           CrashOnOverflow will crash the moment an overflow or failure to allocate
+           memory is detected.
+
+           RecordOverflow will make StringBuilder::hasOverflowed() return true when an
+           overflow or failure to allocate memory is detected.  However, if the user
+           invokes toString(), toStringPreserveCapacity(), toAtomicString(), length(),
+           capacity(), isEmpty(), characters8(), or characters16(), then the StringBuilder
+           will crash regardless because these methods can only meaningfully do their
+           work and return a result if and only if the builder has not overflowed.
+
+           By default, the StringBuilder crashes on overflow (the old behavior) unless it
+           is explicitly constructed with the StringBuilder::OverflowHandler::RecordOverflow
+           enum.
+
+           Design note:
+
+           The StringBuilder can only be put in 1 of the 2 modes at the time of
+           construction.  This means that we could have implemented it as a template
+           that is parameterized on an OverflowHandler class (like CheckedArithmetic)
+           instead of using a runtime check in the ConditionalCrashOnOverflow handler.
+
+           However, I was not able to get clang to export the explicitly instantiated
+           instances (despite the methods having being decorated with WTF_EXPORT_PRIVATE).
+           Since the StringBuilder is a transient object (not stored for a long time on
+           the heap), and the runtime check of the boolean is not that costly compared
+           to other work that StringBuilder routinely does (e.g. memcpy), using the
+           new ConditionalCrashOnOverflow (which does a runtime check to determine to
+           crash or not on overflow) is fine.
+
+           When clang is able to export explicitly instantiated template methods, we can
+           templatize StringBuilder and do away with ConditionalCrashOnOverflow.
+           See https://bugs.webkit.org/show_bug.cgi?id=191050.
+
+        To support the above, we also did:
+
+        2. Enhanced all StringBuilder append and buffer allocation methods to be able to
+           fail without crashing.  This also meant that ...
+
+        3. Added tryReallocate() support to StringImpl.  tryRealloc() was added to
+           FastMalloc, and bmalloc (and related peers) to enable this.
+
+        4. Added ConditionalCrashOnOverflow, which can choose at runtime whether to behave
+           like CrashOnOverflow or RecordOverflow.
+
+        * wtf/CheckedArithmetic.h:
+        (WTF::ConditionalCrashOnOverflow::overflowed):
+        (WTF::ConditionalCrashOnOverflow::shouldCrashOnOverflow const):
+        (WTF::ConditionalCrashOnOverflow::setShouldCrashOnOverflow):
+        (WTF::ConditionalCrashOnOverflow::hasOverflowed const):
+        (WTF::ConditionalCrashOnOverflow::clearOverflow):
+        (WTF::ConditionalCrashOnOverflow::crash):
+        (WTF::RecordOverflow::overflowed):
+        (WTF::Checked::unsafeGet const):
+        * wtf/FastMalloc.cpp:
+        (WTF::tryFastRealloc):
+        * wtf/FastMalloc.h:
+        * wtf/text/StringBuilder.cpp:
+        (WTF::expandedCapacity):
+        (WTF::StringBuilder::reifyString const):
+        (WTF::StringBuilder::resize):
+        (WTF::StringBuilder::allocateBuffer):
+        (WTF::StringBuilder::allocateBufferUpConvert):
+        (WTF::StringBuilder::reallocateBuffer<LChar>):
+        (WTF::StringBuilder::reallocateBuffer<UChar>):
+        (WTF::StringBuilder::reserveCapacity):
+        (WTF::StringBuilder::appendUninitialized):
+        (WTF::StringBuilder::appendUninitializedSlow):
+        (WTF::StringBuilder::append):
+        (WTF::StringBuilder::canShrink const):
+        (WTF::StringBuilder::shrinkToFit):
+        * wtf/text/StringBuilder.h:
+        (WTF::StringBuilder::StringBuilder):
+        (WTF::StringBuilder::didOverflow):
+        (WTF::StringBuilder::hasOverflowed const):
+        (WTF::StringBuilder::crashesOnOverflow const):
+        (WTF::StringBuilder::append):
+        (WTF::StringBuilder::toString):
+        (WTF::StringBuilder::toStringPreserveCapacity const):
+        (WTF::StringBuilder::toAtomicString const):
+        (WTF::StringBuilder::length const):
+        (WTF::StringBuilder::capacity const):
+        (WTF::StringBuilder::operator[] const):
+        (WTF::StringBuilder::swap):
+        (WTF::StringBuilder::getBufferCharacters<UChar>):
+        * wtf/text/StringBuilderJSON.cpp:
+        (WTF::StringBuilder::appendQuotedJSONString):
+        * wtf/text/StringImpl.cpp:
+        (WTF::StringImpl::reallocateInternal):
+        (WTF::StringImpl::reallocate):
+        (WTF::StringImpl::tryReallocate):
+        * wtf/text/StringImpl.h:
+
 2018-10-29  Tim Horton  <timothy_hor...@apple.com>
 
         Modernize WebKit nibs and lprojs for localization's sake

Modified: trunk/Source/WTF/wtf/CheckedArithmetic.h (237576 => 237577)


--- trunk/Source/WTF/wtf/CheckedArithmetic.h	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/wtf/CheckedArithmetic.h	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -70,6 +70,31 @@
     DidNotOverflow
 };
 
+class ConditionalCrashOnOverflow {
+public:
+    void overflowed()
+    {
+        m_overflowed = true;
+        if (m_shouldCrashOnOverflow)
+            crash();
+    }
+
+    bool shouldCrashOnOverflow() const { return m_shouldCrashOnOverflow; }
+    void setShouldCrashOnOverflow(bool value) { m_shouldCrashOnOverflow = value; }
+
+    bool hasOverflowed() const { return m_overflowed; }
+    void clearOverflow() { m_overflowed = false; }
+
+    static NO_RETURN_DUE_TO_CRASH void crash()
+    {
+        CRASH();
+    }
+
+private:
+    bool m_overflowed { false };
+    bool m_shouldCrashOnOverflow { true };
+};
+
 class CrashOnOverflow {
 public:
     static NO_RETURN_DUE_TO_CRASH void overflowed()
@@ -95,11 +120,6 @@
     {
     }
 
-    void overflowed()
-    {
-        m_overflowed = true;
-    }
-
     void clearOverflow()
     {
         m_overflowed = false;
@@ -112,6 +132,7 @@
 
 public:
     bool hasOverflowed() const { return m_overflowed; }
+    void overflowed() { m_overflowed = true; }
 
 private:
     unsigned char m_overflowed;
@@ -206,6 +227,11 @@
     static const CleanType DefaultValue = 0;    
 };
 
+template <typename T> struct RemoveChecked<Checked<T, ConditionalCrashOnOverflow>> {
+    using CleanType = typename RemoveChecked<T>::CleanType;
+    static const CleanType DefaultValue = 0;
+};
+
 template <typename T> struct RemoveChecked<Checked<T, CrashOnOverflow>> {
     typedef typename RemoveChecked<T>::CleanType CleanType;
     static const CleanType DefaultValue = 0;
@@ -631,11 +657,12 @@
     }
 
     // Value accessors. unsafeGet() will crash if there's been an overflow.
-    T unsafeGet() const
+    template<typename U = T>
+    U unsafeGet() const
     {
         if (this->hasOverflowed())
             this->crash();
-        return m_value;
+        return static_cast<U>(m_value);
     }
     
     inline CheckedState safeGet(T& value) const WARN_UNUSED_RETURN
@@ -914,6 +941,7 @@
 using WTF::CheckedInt64;
 using WTF::CheckedUint64;
 using WTF::CheckedSize;
+using WTF::ConditionalCrashOnOverflow;
 using WTF::CrashOnOverflow;
 using WTF::RecordOverflow;
 using WTF::checkedSum;

Modified: trunk/Source/WTF/wtf/FastMalloc.cpp (237576 => 237577)


--- trunk/Source/WTF/wtf/FastMalloc.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/wtf/FastMalloc.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2005, 2007, Google Inc. All rights reserved.
- * Copyright (C) 2005-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2005-2018 Apple Inc. All rights reserved.
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -221,6 +221,12 @@
     return result;
 }
 
+TryMallocReturnValue tryFastRealloc(void* p, size_t n)
+{
+    FAIL_IF_EXCEEDS_LIMIT(n);
+    return realloc(p, n);
+}
+
 void releaseFastMallocFreeMemory() { }
 void releaseFastMallocFreeMemoryForThisThread() { }
     
@@ -341,6 +347,12 @@
     return tryFastZeroedMalloc(checkedSize.unsafeGet());
 }
     
+TryMallocReturnValue tryFastRealloc(void* object, size_t newSize)
+{
+    FAIL_IF_EXCEEDS_LIMIT(newSize);
+    return bmalloc::api::tryRealloc(object, newSize);
+}
+
 void releaseFastMallocFreeMemoryForThisThread()
 {
     bmalloc::api::scavengeThisThread();

Modified: trunk/Source/WTF/wtf/FastMalloc.h (237576 => 237577)


--- trunk/Source/WTF/wtf/FastMalloc.h	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/wtf/FastMalloc.h	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2005-2009, 2015-2016 Apple Inc. All rights reserved.
+ *  Copyright (C) 2005-2018 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -53,6 +53,7 @@
 WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastMalloc(size_t);
 WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastZeroedMalloc(size_t);
 WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastCalloc(size_t numElements, size_t elementSize);
+WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastRealloc(void*, size_t);
 
 WTF_EXPORT_PRIVATE void fastFree(void*);
 

Modified: trunk/Source/WTF/wtf/text/StringBuilder.cpp (237576 => 237577)


--- trunk/Source/WTF/wtf/text/StringBuilder.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/wtf/text/StringBuilder.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
  * Copyright (C) 2012 Google Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,17 +34,20 @@
 
 namespace WTF {
 
+static constexpr unsigned maxCapacity = String::MaxLength + 1;
+
 static unsigned expandedCapacity(unsigned capacity, unsigned requiredLength)
 {
     static const unsigned minimumCapacity = 16;
-    return std::max(requiredLength, std::max(minimumCapacity, capacity * 2));
+    return std::max(requiredLength, std::max(minimumCapacity, std::min(capacity * 2, maxCapacity)));
 }
 
 void StringBuilder::reifyString() const
 {
+    ASSERT(!hasOverflowed());
     // Check if the string already exists.
     if (!m_string.isNull()) {
-        ASSERT(m_string.length() == m_length);
+        ASSERT(m_string.length() == m_length.unsafeGet<unsigned>());
         return;
     }
 
@@ -55,21 +58,28 @@
     }
 
     // Must be valid in the buffer, take a substring (unless string fills the buffer).
-    ASSERT(m_buffer && m_length <= m_buffer->length());
-    if (m_length == m_buffer->length())
+    ASSERT(m_buffer && m_length.unsafeGet<unsigned>() <= m_buffer->length());
+    if (m_length.unsafeGet<unsigned>() == m_buffer->length())
         m_string = m_buffer.get();
     else
-        m_string = StringImpl::createSubstringSharingImpl(*m_buffer, 0, m_length);
+        m_string = StringImpl::createSubstringSharingImpl(*m_buffer, 0, m_length.unsafeGet());
 }
 
 void StringBuilder::resize(unsigned newSize)
 {
+    if (hasOverflowed())
+        return;
+
     // Check newSize < m_length, hence m_length > 0.
-    ASSERT(newSize <= m_length);
-    if (newSize == m_length)
+    unsigned oldLength = m_length.unsafeGet();
+    ASSERT(newSize <= oldLength);
+    if (newSize == oldLength)
         return;
-    ASSERT(m_length);
+    ASSERT(oldLength);
 
+    m_length = newSize;
+    ASSERT(!hasOverflowed());
+
     // If there is a buffer, we only need to duplicate it if it has more than one ref.
     if (m_buffer) {
         m_string = String(); // Clear the string to remove the reference to m_buffer if any before checking the reference count of m_buffer.
@@ -79,16 +89,14 @@
             else
                 allocateBuffer(m_buffer->characters16(), m_buffer->length());
         }
-        m_length = newSize;
-        ASSERT(m_buffer->length() >= m_length);
+        ASSERT(hasOverflowed() || m_buffer->length() >= m_length.unsafeGet<unsigned>());
         return;
     }
 
     // Since m_length && !m_buffer, the string must be valid in m_string, and m_string.length() > 0.
     ASSERT(!m_string.isEmpty());
-    ASSERT(m_length == m_string.length());
+    ASSERT(oldLength == m_string.length());
     ASSERT(newSize < m_string.length());
-    m_length = newSize;
     m_string = StringImpl::createSubstringSharingImpl(*m_string.impl(), 0, newSize);
 }
 
@@ -96,10 +104,13 @@
 // or m_buffer, neither will be reassigned until the copy has completed).
 void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requiredLength)
 {
+    ASSERT(!hasOverflowed());
     ASSERT(m_is8Bit);
     // Copy the existing data into a new buffer, set result to point to the end of the existing data.
-    auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8);
-    memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length) * sizeof(LChar)); // This can't overflow.
+    auto buffer = StringImpl::tryCreateUninitialized(requiredLength, m_bufferCharacters8);
+    if (UNLIKELY(!buffer))
+        return didOverflow();
+    memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length.unsafeGet()) * sizeof(LChar)); // This can't overflow.
     
     // Update the builder state.
     m_buffer = WTFMove(buffer);
@@ -111,10 +122,13 @@
 // or m_buffer,  neither will be reassigned until the copy has completed).
 void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requiredLength)
 {
+    ASSERT(!hasOverflowed());
     ASSERT(!m_is8Bit);
     // Copy the existing data into a new buffer, set result to point to the end of the existing data.
-    auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
-    memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length) * sizeof(UChar)); // This can't overflow.
+    auto buffer = StringImpl::tryCreateUninitialized(requiredLength, m_bufferCharacters16);
+    if (UNLIKELY(!buffer))
+        return didOverflow();
+    memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length.unsafeGet()) * sizeof(UChar)); // This can't overflow.
     
     // Update the builder state.
     m_buffer = WTFMove(buffer);
@@ -126,11 +140,15 @@
 // from either m_string or m_buffer, neither will be reassigned until the copy has completed).
 void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength)
 {
+    ASSERT(!hasOverflowed());
     ASSERT(m_is8Bit);
-    ASSERT(requiredLength >= m_length);
+    unsigned length = m_length.unsafeGet();
+    ASSERT(requiredLength <= maxCapacity && requiredLength >= length);
     // Copy the existing data into a new buffer, set result to point to the end of the existing data.
-    auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
-    for (unsigned i = 0; i < m_length; ++i)
+    auto buffer = StringImpl::tryCreateUninitialized(requiredLength, m_bufferCharacters16);
+    if (UNLIKELY(!buffer))
+        return didOverflow(); // Treat a failure to allcoate as an overflow.
+    for (unsigned i = 0; i < length; ++i)
         m_bufferCharacters16[i] = currentCharacters[i];
     
     m_is8Bit = false;
@@ -151,11 +169,14 @@
     ASSERT(m_is8Bit);
     ASSERT(m_buffer->is8Bit());
     
-    if (m_buffer->hasOneRef())
-        m_buffer = StringImpl::reallocate(m_buffer.releaseNonNull(), requiredLength, m_bufferCharacters8);
-    else
+    if (m_buffer->hasOneRef()) {
+        auto expectedStringImpl = StringImpl::tryReallocate(m_buffer.releaseNonNull(), requiredLength, m_bufferCharacters8);
+        if (UNLIKELY(!expectedStringImpl))
+            return didOverflow();
+        m_buffer = WTFMove(expectedStringImpl.value());
+    } else
         allocateBuffer(m_buffer->characters8(), requiredLength);
-    ASSERT(m_buffer->length() == requiredLength);
+    ASSERT(hasOverflowed() || m_buffer->length() == requiredLength);
 }
 
 template <>
@@ -167,15 +188,21 @@
     
     if (m_buffer->is8Bit())
         allocateBufferUpConvert(m_buffer->characters8(), requiredLength);
-    else if (m_buffer->hasOneRef())
-        m_buffer = StringImpl::reallocate(m_buffer.releaseNonNull(), requiredLength, m_bufferCharacters16);
-    else
+    else if (m_buffer->hasOneRef()) {
+        auto expectedStringImpl = StringImpl::tryReallocate(m_buffer.releaseNonNull(), requiredLength, m_bufferCharacters16);
+        if (UNLIKELY(!expectedStringImpl))
+            return didOverflow();
+        m_buffer = WTFMove(expectedStringImpl.value());
+    } else
         allocateBuffer(m_buffer->characters16(), requiredLength);
-    ASSERT(m_buffer->length() == requiredLength);
+    ASSERT(hasOverflowed() || m_buffer->length() == requiredLength);
 }
 
 void StringBuilder::reserveCapacity(unsigned newCapacity)
 {
+    if (hasOverflowed())
+        return;
+    ASSERT(newCapacity <= String::MaxLength);
     if (m_buffer) {
         // If there is already a buffer, then grow if necessary.
         if (newCapacity > m_buffer->length()) {
@@ -186,8 +213,9 @@
         }
     } else {
         // Grow the string, if necessary.
-        if (newCapacity > m_length) {
-            if (!m_length) {
+        unsigned length = m_length.unsafeGet();
+        if (newCapacity > length) {
+            if (!length) {
                 LChar* nullPlaceholder = 0;
                 allocateBuffer(nullPlaceholder, newCapacity);
             } else if (m_string.is8Bit())
@@ -196,11 +224,12 @@
                 allocateBuffer(m_string.characters16(), newCapacity);
         }
     }
-    ASSERT(!newCapacity || m_buffer->length() >= newCapacity);
+    ASSERT(hasOverflowed() || !newCapacity || m_buffer->length() >= newCapacity);
 }
 
 // Make 'length' additional capacity be available in m_buffer, update m_string & m_length,
 // return a pointer to the newly allocated storage.
+// Returns nullptr if the size of the new builder would have overflowed
 template <typename CharType>
 ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length)
 {
@@ -207,20 +236,22 @@
     ASSERT(length);
 
     // Calculate the new size of the builder after appending.
-    unsigned requiredLength = length + m_length;
-    if (requiredLength < length)
-        CRASH();
+    CheckedInt32 requiredLength = m_length + length;
+    if (requiredLength.hasOverflowed()) {
+        didOverflow();
+        return nullptr;
+    }
 
-    if ((m_buffer) && (requiredLength <= m_buffer->length())) {
+    if (m_buffer && (requiredLength.unsafeGet<unsigned>() <= m_buffer->length())) {
         // If the buffer is valid it must be at least as long as the current builder contents!
-        ASSERT(m_buffer->length() >= m_length);
-        unsigned currentLength = m_length;
+        ASSERT(m_buffer->length() >= m_length.unsafeGet<unsigned>());
+        unsigned currentLength = m_length.unsafeGet();
         m_string = String();
         m_length = requiredLength;
         return getBufferCharacters<CharType>() + currentLength;
     }
     
-    return appendUninitializedSlow<CharType>(requiredLength);
+    return appendUninitializedSlow<CharType>(requiredLength.unsafeGet());
 }
 
 // Make 'length' additional capacity be available in m_buffer, update m_string & m_length,
@@ -228,27 +259,31 @@
 template <typename CharType>
 CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength)
 {
+    ASSERT(!hasOverflowed());
     ASSERT(requiredLength);
 
     if (m_buffer) {
         // If the buffer is valid it must be at least as long as the current builder contents!
-        ASSERT(m_buffer->length() >= m_length);
+        ASSERT(m_buffer->length() >= m_length.unsafeGet<unsigned>());
         
         reallocateBuffer<CharType>(expandedCapacity(capacity(), requiredLength));
     } else {
-        ASSERT(m_string.length() == m_length);
+        ASSERT(m_string.length() == m_length.unsafeGet<unsigned>());
         allocateBuffer(m_length ? m_string.characters<CharType>() : 0, expandedCapacity(capacity(), requiredLength));
     }
-    
-    CharType* result = getBufferCharacters<CharType>() + m_length;
+    if (UNLIKELY(hasOverflowed()))
+        return nullptr;
+
+    CharType* result = getBufferCharacters<CharType>() + m_length.unsafeGet();
     m_length = requiredLength;
-    ASSERT(m_buffer->length() >= m_length);
+    ASSERT(!hasOverflowed());
+    ASSERT(m_buffer->length() >= m_length.unsafeGet<unsigned>());
     return result;
 }
 
 void StringBuilder::append(const UChar* characters, unsigned length)
 {
-    if (!length)
+    if (!length || hasOverflowed())
         return;
 
     ASSERT(characters);
@@ -257,40 +292,50 @@
         if (length == 1 && !(*characters & ~0xff)) {
             // Append as 8 bit character
             LChar lChar = static_cast<LChar>(*characters);
-            append(&lChar, 1);
-            return;
+            return append(&lChar, 1);
         }
 
         // Calculate the new size of the builder after appending.
-        unsigned requiredLength = length + m_length;
-        if (requiredLength < length)
-            CRASH();
+        CheckedInt32 requiredLength = m_length + length;
+        if (requiredLength.hasOverflowed())
+            return didOverflow();
         
         if (m_buffer) {
             // If the buffer is valid it must be at least as long as the current builder contents!
-            ASSERT(m_buffer->length() >= m_length);
-            
-            allocateBufferUpConvert(m_buffer->characters8(), expandedCapacity(capacity(), requiredLength));
+            ASSERT(m_buffer->length() >= m_length.unsafeGet<unsigned>());
+            allocateBufferUpConvert(m_buffer->characters8(), expandedCapacity(capacity(), requiredLength.unsafeGet()));
         } else {
-            ASSERT(m_string.length() == m_length);
-            allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), expandedCapacity(capacity(), requiredLength));
+            ASSERT(m_string.length() == m_length.unsafeGet<unsigned>());
+            allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), expandedCapacity(capacity(), requiredLength.unsafeGet()));
         }
+        if (UNLIKELY(hasOverflowed()))
+            return;
 
-        memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(length) * sizeof(UChar));
+        memcpy(m_bufferCharacters16 + m_length.unsafeGet<unsigned>(), characters, static_cast<size_t>(length) * sizeof(UChar));
         m_length = requiredLength;
-    } else
-        memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_t>(length) * sizeof(UChar));
-    ASSERT(m_buffer->length() >= m_length);
+    } else {
+        UChar* dest = appendUninitialized<UChar>(length);
+        if (!dest)
+            return;
+        memcpy(dest, characters, static_cast<size_t>(length) * sizeof(UChar));
+    }
+    ASSERT(!hasOverflowed());
+    ASSERT(m_buffer->length() >= m_length.unsafeGet<unsigned>());
 }
 
 void StringBuilder::append(const LChar* characters, unsigned length)
 {
-    if (!length)
+    if (!length || hasOverflowed())
         return;
+
     ASSERT(characters);
 
     if (m_is8Bit) {
         LChar* dest = appendUninitialized<LChar>(length);
+        if (!dest) {
+            ASSERT(hasOverflowed());
+            return;
+        }
         if (length > 8)
             memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar));
         else {
@@ -300,6 +345,10 @@
         }
     } else {
         UChar* dest = appendUninitialized<UChar>(length);
+        if (!dest) {
+            ASSERT(hasOverflowed());
+            return;
+        }
         const LChar* end = characters + length;
         while (characters < end)
             *(dest++) = *(characters++);
@@ -370,8 +419,11 @@
 
 bool StringBuilder::canShrink() const
 {
+    if (hasOverflowed())
+        return false;
     // Only shrink the buffer if it's less than 80% full. Need to tune this heuristic!
-    return m_buffer && m_buffer->length() > (m_length + (m_length >> 2));
+    unsigned length = m_length.unsafeGet();
+    return m_buffer && m_buffer->length() > (length + (length >> 2));
 }
 
 void StringBuilder::shrinkToFit()
@@ -378,9 +430,10 @@
 {
     if (canShrink()) {
         if (m_is8Bit)
-            reallocateBuffer<LChar>(m_length);
+            reallocateBuffer<LChar>(m_length.unsafeGet());
         else
-            reallocateBuffer<UChar>(m_length);
+            reallocateBuffer<UChar>(m_length.unsafeGet());
+        ASSERT(!hasOverflowed());
         m_string = WTFMove(m_buffer);
     }
 }

Modified: trunk/Source/WTF/wtf/text/StringBuilder.h (237576 => 237577)


--- trunk/Source/WTF/wtf/text/StringBuilder.h	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/wtf/text/StringBuilder.h	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2010, 2012-2013, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
  * Copyright (C) 2012 Google Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,6 +26,7 @@
 
 #pragma once
 
+#include <wtf/CheckedArithmetic.h>
 #include <wtf/text/AtomicString.h>
 #include <wtf/text/IntegerToStringConversion.h>
 #include <wtf/text/StringView.h>
@@ -33,18 +34,39 @@
 
 namespace WTF {
 
+// StringBuilder currently uses a Checked<int32_t, ConditionalCrashOnOverflow> for m_length.
+// Ideally, we would want to make StringBuilder a template with an OverflowHandler parameter, and
+// m_length can be instantiated based on that OverflowHandler instead. However, currently, we're
+// not able to get clang to export explicitly instantiated template methods (which would be needed
+// if we templatize StringBuilder). As a workaround, we use the ConditionalCrashOnOverflow handler
+// instead to do a runtime check on whether it should crash on overflows or not.
+//
+// When clang is able to export explicitly instantiated template methods, we can templatize
+// StringBuilder and do away with ConditionalCrashOnOverflow.
+// See https://bugs.webkit.org/show_bug.cgi?id=191050.
+
 class StringBuilder {
     // Disallow copying since it's expensive and we don't want code to do it by accident.
     WTF_MAKE_NONCOPYABLE(StringBuilder);
 
 public:
-    StringBuilder()
+    enum class OverflowHandler {
+        CrashOnOverflow,
+        RecordOverflow
+    };
+
+    StringBuilder(OverflowHandler handler = OverflowHandler::CrashOnOverflow)
         : m_bufferCharacters8(nullptr)
     {
+        m_length.setShouldCrashOnOverflow(handler == OverflowHandler::CrashOnOverflow);
     }
     StringBuilder(StringBuilder&&) = default;
     StringBuilder& operator=(StringBuilder&&) = default;
 
+    ALWAYS_INLINE void didOverflow() { m_length.overflowed(); }
+    ALWAYS_INLINE bool hasOverflowed() const { return m_length.hasOverflowed(); }
+    ALWAYS_INLINE bool crashesOnOverflow() const { return m_length.shouldCrashOnOverflow(); }
+
     WTF_EXPORT_PRIVATE void append(const UChar*, unsigned);
     WTF_EXPORT_PRIVATE void append(const LChar*, unsigned);
 
@@ -57,6 +79,9 @@
 
     void append(const String& string)
     {
+        if (hasOverflowed())
+            return;
+
         if (!string.length())
             return;
 
@@ -77,6 +102,11 @@
 
     void append(const StringBuilder& other)
     {
+        if (hasOverflowed())
+            return;
+        if (other.hasOverflowed())
+            return didOverflow();
+
         if (!other.m_length)
             return;
 
@@ -89,9 +119,9 @@
         }
 
         if (other.is8Bit())
-            append(other.characters8(), other.m_length);
+            append(other.characters8(), other.m_length.unsafeGet());
         else
-            append(other.characters16(), other.m_length);
+            append(other.characters16(), other.m_length.unsafeGet());
     }
 
     void append(StringView stringView)
@@ -131,14 +161,19 @@
 
     void append(UChar c)
     {
-        if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) {
+        if (hasOverflowed())
+            return;
+        unsigned length = m_length.unsafeGet<unsigned>();
+        if (m_buffer && length < m_buffer->length() && m_string.isNull()) {
             if (!m_is8Bit) {
-                m_bufferCharacters16[m_length++] = c;
+                m_bufferCharacters16[length] = c;
+                m_length++;
                 return;
             }
 
             if (!(c & ~0xff)) {
-                m_bufferCharacters8[m_length++] = static_cast<LChar>(c);
+                m_bufferCharacters8[length] = static_cast<LChar>(c);
+                m_length++;
                 return;
             }
         }
@@ -147,11 +182,15 @@
 
     void append(LChar c)
     {
-        if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) {
+        if (hasOverflowed())
+            return;
+        unsigned length = m_length.unsafeGet<unsigned>();
+        if (m_buffer && length < m_buffer->length() && m_string.isNull()) {
             if (m_is8Bit)
-                m_bufferCharacters8[m_length++] = c;
+                m_bufferCharacters8[length] = c;
             else
-                m_bufferCharacters16[m_length++] = c;
+                m_bufferCharacters16[length] = c;
+            m_length++;
         } else
             append(&c, 1);
     }
@@ -171,7 +210,7 @@
         append(U16_TRAIL(c));
     }
 
-    WTF_EXPORT_PRIVATE bool appendQuotedJSONString(const String&);
+    WTF_EXPORT_PRIVATE void appendQuotedJSONString(const String&);
 
     template<unsigned characterCount>
     ALWAYS_INLINE void appendLiteral(const char (&characters)[characterCount]) { append(characters, characterCount - 1); }
@@ -188,6 +227,7 @@
 
     String toString()
     {
+        RELEASE_ASSERT(!hasOverflowed());
         shrinkToFit();
         if (m_string.isNull())
             reifyString();
@@ -196,6 +236,7 @@
 
     const String& toStringPreserveCapacity() const
     {
+        RELEASE_ASSERT(!hasOverflowed());
         if (m_string.isNull())
             reifyString();
         return m_string;
@@ -203,6 +244,7 @@
 
     AtomicString toAtomicString() const
     {
+        RELEASE_ASSERT(!hasOverflowed());
         if (!m_length)
             return emptyAtom();
 
@@ -217,12 +259,13 @@
             return AtomicString(m_string);
 
         ASSERT(m_buffer);
-        return AtomicString(m_buffer.get(), 0, m_length);
+        return AtomicString(m_buffer.get(), 0, m_length.unsafeGet());
     }
 
     unsigned length() const
     {
-        return m_length;
+        RELEASE_ASSERT(!hasOverflowed());
+        return m_length.unsafeGet();
     }
 
     bool isEmpty() const { return !m_length; }
@@ -231,7 +274,8 @@
 
     unsigned capacity() const
     {
-        return m_buffer ? m_buffer->length() : m_length;
+        RELEASE_ASSERT(!hasOverflowed());
+        return m_buffer ? m_buffer->length() : m_length.unsafeGet();
     }
 
     WTF_EXPORT_PRIVATE void resize(unsigned newSize);
@@ -242,7 +286,7 @@
 
     UChar operator[](unsigned i) const
     {
-        ASSERT_WITH_SECURITY_IMPLICATION(i < m_length);
+        RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!hasOverflowed() && i < m_length.unsafeGet<unsigned>());
         if (m_is8Bit)
             return characters8()[i];
         return characters16()[i];
@@ -288,7 +332,7 @@
         m_buffer.swap(stringBuilder.m_buffer);
         std::swap(m_is8Bit, stringBuilder.m_is8Bit);
         std::swap(m_bufferCharacters8, stringBuilder.m_bufferCharacters8);
-        ASSERT(!m_buffer || m_buffer->length() >= m_length);
+        ASSERT(!m_buffer || hasOverflowed() || m_buffer->length() >= m_length.unsafeGet<unsigned>());
     }
 
 private:
@@ -311,7 +355,8 @@
         LChar* m_bufferCharacters8;
         UChar* m_bufferCharacters16;
     };
-    unsigned m_length { 0 };
+    static_assert(String::MaxLength == std::numeric_limits<int32_t>::max(), "");
+    Checked<int32_t, ConditionalCrashOnOverflow> m_length;
     bool m_is8Bit { true };
 };
 
@@ -327,7 +372,7 @@
 {
     ASSERT(!m_is8Bit);
     return m_bufferCharacters16;
-}    
+}
 
 template <typename CharType>
 bool equal(const StringBuilder& s, const CharType* buffer, unsigned length)

Modified: trunk/Source/WTF/wtf/text/StringBuilderJSON.cpp (237576 => 237577)


--- trunk/Source/WTF/wtf/text/StringBuilderJSON.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/wtf/text/StringBuilderJSON.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
  * Copyright (C) 2012 Google Inc. All rights reserved.
  * Copyright (C) 2017 Yusuke Suzuki <utatane....@gmail.com>. All rights reserved.
  * Copyright (C) 2017 Mozilla Foundation. All rights reserved.
@@ -74,8 +74,10 @@
     }
 }
 
-bool StringBuilder::appendQuotedJSONString(const String& string)
+void StringBuilder::appendQuotedJSONString(const String& string)
 {
+    if (hasOverflowed())
+        return;
     // Make sure we have enough buffer space to append this string without having
     // to worry about reallocating in the middle.
     // The 2 is for the '"' quotes on each end.
@@ -85,7 +87,7 @@
     maximumCapacityRequired += 2 + stringLength * 6;
     unsigned allocationSize;
     if (CheckedState::DidOverflow == maximumCapacityRequired.safeGet(allocationSize))
-        return false;
+        return didOverflow();
     // This max() is here to allow us to allocate sizes between the range [2^31, 2^32 - 2] because roundUpToPowerOfTwo(1<<31 + some int smaller than 1<<31) == 0.
     // FIXME: roundUpToPowerOfTwo should take Checked<unsigned> and abort if it fails to round up.
     // https://bugs.webkit.org/show_bug.cgi?id=176086
@@ -92,24 +94,26 @@
     allocationSize = std::max(allocationSize, roundUpToPowerOfTwo(allocationSize));
 
     // Allocating this much will definitely fail.
-    if (allocationSize >= 0x80000000)
-        return false;
+    if (allocationSize > String::MaxLength)
+        return didOverflow();
 
     if (is8Bit() && !string.is8Bit())
         allocateBufferUpConvert(m_bufferCharacters8, allocationSize);
     else
         reserveCapacity(allocationSize);
+    if (UNLIKELY(hasOverflowed()))
+        return;
     ASSERT(m_buffer->length() >= allocationSize);
 
     if (is8Bit()) {
         ASSERT(string.is8Bit());
-        LChar* output = m_bufferCharacters8 + m_length;
+        LChar* output = m_bufferCharacters8 + m_length.unsafeGet<unsigned>();
         *output++ = '"';
         appendQuotedJSONStringInternal(output, string.characters8(), string.length());
         *output++ = '"';
         m_length = output - m_bufferCharacters8;
     } else {
-        UChar* output = m_bufferCharacters16 + m_length;
+        UChar* output = m_bufferCharacters16 + m_length.unsafeGet<unsigned>();
         *output++ = '"';
         if (string.is8Bit())
             appendQuotedJSONStringInternal(output, string.characters8(), string.length());
@@ -118,8 +122,8 @@
         *output++ = '"';
         m_length = output - m_bufferCharacters16;
     }
-    ASSERT(m_buffer->length() >= m_length);
-    return true;
+    ASSERT(!hasOverflowed());
+    ASSERT(m_buffer->length() >= m_length.unsafeGet<unsigned>());
 }
 
 } // namespace WTF

Modified: trunk/Source/WTF/wtf/text/StringImpl.cpp (237576 => 237577)


--- trunk/Source/WTF/wtf/text/StringImpl.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/wtf/text/StringImpl.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll (kn...@kde.org)
  *           (C) 1999 Antti Koivisto (koivi...@kde.org)
  *           (C) 2001 Dirk Mueller ( muel...@kde.org )
- * Copyright (C) 2003-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2018 Apple Inc. All rights reserved.
  * Copyright (C) 2006 Andrew Wellington (pro...@wiretapped.net)
  *
  * This library is free software; you can redistribute it and/or
@@ -211,7 +211,7 @@
     return createUninitializedInternal(length, data);
 }
 
-template<typename CharacterType> inline Ref<StringImpl> StringImpl::reallocateInternal(Ref<StringImpl>&& originalString, unsigned length, CharacterType*& data)
+template<typename CharacterType> inline Expected<Ref<StringImpl>, UTF8ConversionError> StringImpl::reallocateInternal(Ref<StringImpl>&& originalString, unsigned length, CharacterType*& data)
 {
     ASSERT(originalString->hasOneRef());
     ASSERT(originalString->bufferOwnership() == BufferInternal);
@@ -218,15 +218,17 @@
 
     if (!length) {
         data = ""
-        return *empty();
+        return Ref<StringImpl>(*empty());
     }
 
     // Same as createUninitialized() except here we use fastRealloc.
     if (length > ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) / sizeof(CharacterType)))
-        CRASH();
+        return makeUnexpected(UTF8ConversionError::OutOfMemory);
 
     originalString->~StringImpl();
-    auto* string = static_cast<StringImpl*>(fastRealloc(&originalString.leakRef(), allocationSize<CharacterType>(length)));
+    StringImpl* string;
+    if (!tryFastRealloc(&originalString.leakRef(), allocationSize<CharacterType>(length)).getValue(string))
+        return makeUnexpected(UTF8ConversionError::OutOfMemory);
 
     data = ""
     return constructInternal<CharacterType>(*string, length);
@@ -234,11 +236,25 @@
 
 Ref<StringImpl> StringImpl::reallocate(Ref<StringImpl>&& originalString, unsigned length, LChar*& data)
 {
+    auto expectedStringImpl = tryReallocate(WTFMove(originalString), length, data);
+    RELEASE_ASSERT(expectedStringImpl);
+    return WTFMove(expectedStringImpl.value());
+}
+
+Ref<StringImpl> StringImpl::reallocate(Ref<StringImpl>&& originalString, unsigned length, UChar*& data)
+{
+    auto expectedStringImpl = tryReallocate(WTFMove(originalString), length, data);
+    RELEASE_ASSERT(expectedStringImpl);
+    return WTFMove(expectedStringImpl.value());
+}
+
+Expected<Ref<StringImpl>, UTF8ConversionError> StringImpl::tryReallocate(Ref<StringImpl>&& originalString, unsigned length, LChar*& data)
+{
     ASSERT(originalString->is8Bit());
     return reallocateInternal(WTFMove(originalString), length, data);
 }
 
-Ref<StringImpl> StringImpl::reallocate(Ref<StringImpl>&& originalString, unsigned length, UChar*& data)
+Expected<Ref<StringImpl>, UTF8ConversionError> StringImpl::tryReallocate(Ref<StringImpl>&& originalString, unsigned length, UChar*& data)
 {
     ASSERT(!originalString->is8Bit());
     return reallocateInternal(WTFMove(originalString), length, data);

Modified: trunk/Source/WTF/wtf/text/StringImpl.h (237576 => 237577)


--- trunk/Source/WTF/wtf/text/StringImpl.h	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/WTF/wtf/text/StringImpl.h	2018-10-30 02:58:51 UTC (rev 237577)
@@ -254,6 +254,8 @@
     // the originalString can't be used after this function.
     static Ref<StringImpl> reallocate(Ref<StringImpl>&& originalString, unsigned length, LChar*& data);
     static Ref<StringImpl> reallocate(Ref<StringImpl>&& originalString, unsigned length, UChar*& data);
+    static Expected<Ref<StringImpl>, UTF8ConversionError> tryReallocate(Ref<StringImpl>&& originalString, unsigned length, LChar*& data);
+    static Expected<Ref<StringImpl>, UTF8ConversionError> tryReallocate(Ref<StringImpl>&& originalString, unsigned length, UChar*& data);
 
     static unsigned flagsOffset() { return OBJECT_OFFSETOF(StringImpl, m_hashAndFlags); }
     static unsigned flagIs8Bit() { return s_hashFlag8BitBuffer; }
@@ -510,7 +512,7 @@
     template<typename CharacterType> static Ref<StringImpl> constructInternal(StringImpl&, unsigned);
     template<typename CharacterType> static Ref<StringImpl> createUninitializedInternal(unsigned, CharacterType*&);
     template<typename CharacterType> static Ref<StringImpl> createUninitializedInternalNonEmpty(unsigned, CharacterType*&);
-    template<typename CharacterType> static Ref<StringImpl> reallocateInternal(Ref<StringImpl>&&, unsigned, CharacterType*&);
+    template<typename CharacterType> static Expected<Ref<StringImpl>, UTF8ConversionError> reallocateInternal(Ref<StringImpl>&&, unsigned, CharacterType*&);
     template<typename CharacterType> static Ref<StringImpl> createInternal(const CharacterType*, unsigned);
     WTF_EXPORT_PRIVATE NEVER_INLINE unsigned hashSlowCase() const;
 

Modified: trunk/Source/bmalloc/ChangeLog (237576 => 237577)


--- trunk/Source/bmalloc/ChangeLog	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/bmalloc/ChangeLog	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,3 +1,24 @@
+2018-10-29  Mark Lam  <mark....@apple.com>
+
+        Correctly detect string overflow when using the 'Function' constructor.
+        https://bugs.webkit.org/show_bug.cgi?id=184883
+        <rdar://problem/36320331>
+
+        Reviewed by Saam Barati.
+
+        * bmalloc/Allocator.cpp:
+        (bmalloc::Allocator::reallocate):
+        (bmalloc::Allocator::tryReallocate):
+        (bmalloc::Allocator::reallocateImpl):
+        * bmalloc/Allocator.h:
+        * bmalloc/Cache.h:
+        (bmalloc::Cache::tryReallocate):
+        * bmalloc/DebugHeap.cpp:
+        (bmalloc::DebugHeap::realloc):
+        * bmalloc/DebugHeap.h:
+        * bmalloc/bmalloc.h:
+        (bmalloc::api::tryRealloc):
+
 2018-10-25  Ross Kirsling  <ross.kirsl...@sony.com>
 
         Cleanup: inline constexpr is redundant as constexpr implies inline

Modified: trunk/Source/bmalloc/bmalloc/Allocator.cpp (237576 => 237577)


--- trunk/Source/bmalloc/bmalloc/Allocator.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/bmalloc/bmalloc/Allocator.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -95,8 +95,20 @@
 
 void* Allocator::reallocate(void* object, size_t newSize)
 {
+    bool crashOnFailure = true;
+    return reallocateImpl(object, newSize, crashOnFailure);
+}
+
+void* Allocator::tryReallocate(void* object, size_t newSize)
+{
+    bool crashOnFailure = false;
+    return reallocateImpl(object, newSize, crashOnFailure);
+}
+
+void* Allocator::reallocateImpl(void* object, size_t newSize, bool crashOnFailure)
+{
     if (m_debugHeap)
-        return m_debugHeap->realloc(object, newSize);
+        return m_debugHeap->realloc(object, newSize, crashOnFailure);
 
     size_t oldSize = 0;
     switch (objectType(m_heap.kind(), object)) {
@@ -121,7 +133,14 @@
     }
     }
 
-    void* result = allocate(newSize);
+    void* result = nullptr;
+    if (crashOnFailure)
+        result = allocate(newSize);
+    else {
+        result = tryAllocate(newSize);
+        if (!result)
+            return nullptr;
+    }
     size_t copySize = std::min(oldSize, newSize);
     memcpy(result, object, copySize);
     m_deallocator.deallocate(object);

Modified: trunk/Source/bmalloc/bmalloc/Allocator.h (237576 => 237577)


--- trunk/Source/bmalloc/bmalloc/Allocator.h	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/bmalloc/bmalloc/Allocator.h	2018-10-30 02:58:51 UTC (rev 237577)
@@ -47,6 +47,7 @@
     void* allocate(size_t);
     void* tryAllocate(size_t alignment, size_t);
     void* allocate(size_t alignment, size_t);
+    void* tryReallocate(void*, size_t);
     void* reallocate(void*, size_t);
 
     void scavenge();
@@ -53,7 +54,8 @@
 
 private:
     void* allocateImpl(size_t alignment, size_t, bool crashOnFailure);
-    
+    void* reallocateImpl(void*, size_t, bool crashOnFailure);
+
     bool allocateFastCase(size_t, void*&);
     BEXPORT void* allocateSlowCase(size_t);
     

Modified: trunk/Source/bmalloc/bmalloc/Cache.h (237576 => 237577)


--- trunk/Source/bmalloc/bmalloc/Cache.h	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/bmalloc/bmalloc/Cache.h	2018-10-30 02:58:51 UTC (rev 237577)
@@ -43,6 +43,7 @@
     static void* tryAllocate(HeapKind, size_t alignment, size_t);
     static void* allocate(HeapKind, size_t alignment, size_t);
     static void deallocate(HeapKind, void*);
+    static void* tryReallocate(HeapKind, void*, size_t);
     static void* reallocate(HeapKind, void*, size_t);
 
     static void scavenge(HeapKind);
@@ -103,6 +104,14 @@
     return caches->at(mapToActiveHeapKindAfterEnsuringGigacage(heapKind)).deallocator().deallocate(object);
 }
 
+inline void* Cache::tryReallocate(HeapKind heapKind, void* object, size_t newSize)
+{
+    PerHeapKind<Cache>* caches = PerThread<PerHeapKind<Cache>>::getFastCase();
+    if (!caches)
+        return reallocateSlowCaseNullCache(heapKind, object, newSize);
+    return caches->at(mapToActiveHeapKindAfterEnsuringGigacage(heapKind)).allocator().tryReallocate(object, newSize);
+}
+
 inline void* Cache::reallocate(HeapKind heapKind, void* object, size_t newSize)
 {
     PerHeapKind<Cache>* caches = PerThread<PerHeapKind<Cache>>::getFastCase();

Modified: trunk/Source/bmalloc/bmalloc/DebugHeap.cpp (237576 => 237577)


--- trunk/Source/bmalloc/bmalloc/DebugHeap.cpp	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/bmalloc/bmalloc/DebugHeap.cpp	2018-10-30 02:58:51 UTC (rev 237577)
@@ -59,10 +59,10 @@
     return result;
 }
 
-void* DebugHeap::realloc(void* object, size_t size)
+void* DebugHeap::realloc(void* object, size_t size, bool crashOnFailure)
 {
     void* result = malloc_zone_realloc(m_zone, object, size);
-    if (!result)
+    if (!result && crashOnFailure)
         BCRASH();
     return result;
 }
@@ -98,10 +98,10 @@
     return result;
 }
 
-void* DebugHeap::realloc(void* object, size_t size)
+void* DebugHeap::realloc(void* object, size_t size, bool crashOnFailure)
 {
     void* result = ::realloc(object, size);
-    if (!result)
+    if (!result && crashOnFailure)
         BCRASH();
     return result;
 }

Modified: trunk/Source/bmalloc/bmalloc/DebugHeap.h (237576 => 237577)


--- trunk/Source/bmalloc/bmalloc/DebugHeap.h	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/bmalloc/bmalloc/DebugHeap.h	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -41,7 +41,7 @@
     
     void* malloc(size_t);
     void* memalign(size_t alignment, size_t, bool crashOnFailure);
-    void* realloc(void*, size_t);
+    void* realloc(void*, size_t, bool crashOnFailure);
     void free(void*);
     
     void* memalignLarge(size_t alignment, size_t);

Modified: trunk/Source/bmalloc/bmalloc/bmalloc.h (237576 => 237577)


--- trunk/Source/bmalloc/bmalloc/bmalloc.h	2018-10-30 02:51:23 UTC (rev 237576)
+++ trunk/Source/bmalloc/bmalloc/bmalloc.h	2018-10-30 02:58:51 UTC (rev 237577)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -63,6 +63,12 @@
     return Cache::allocate(kind, alignment, size);
 }
 
+// Returns null on failure.
+inline void* tryRealloc(void* object, size_t newSize, HeapKind kind = HeapKind::Primary)
+{
+    return Cache::tryReallocate(kind, object, newSize);
+}
+
 // Crashes on failure.
 inline void* realloc(void* object, size_t newSize, HeapKind kind = HeapKind::Primary)
 {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to