Title: [259646] trunk
Revision
259646
Author
ysuz...@apple.com
Date
2020-04-07 11:04:57 -0700 (Tue, 07 Apr 2020)

Log Message

[JSC] ScopedArgumentsTable should handle OOM in tolerant manner
https://bugs.webkit.org/show_bug.cgi?id=210126

Reviewed by Mark Lam.

JSTests:

* stress/scoped-arguments-table-should-be-tolerant-for-oom.js: Added.
(canThrow):
(bar):
(get bar):
(foo):
(i.canThrow):

Source/_javascript_Core:

This patch makes ScopedArgumentsTable allocations OOM tolerant to throw OOM error when allocation fails.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* runtime/CachedTypes.cpp:
(JSC::CachedScopedArgumentsTable::decode const):
* runtime/ScopedArguments.cpp:
(JSC::ScopedArguments::unmapArgument):
* runtime/ScopedArgumentsTable.cpp:
(JSC::ScopedArgumentsTable::tryClone):
(JSC::ScopedArgumentsTable::trySetLength):
(JSC::ScopedArgumentsTable::trySet):
(JSC::ScopedArgumentsTable::clone): Deleted.
(JSC::ScopedArgumentsTable::setLength): Deleted.
(JSC::ScopedArgumentsTable::set): Deleted.
* runtime/ScopedArgumentsTable.h:
* runtime/SymbolTable.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (259645 => 259646)


--- trunk/JSTests/ChangeLog	2020-04-07 17:39:24 UTC (rev 259645)
+++ trunk/JSTests/ChangeLog	2020-04-07 18:04:57 UTC (rev 259646)
@@ -1,5 +1,19 @@
 2020-04-07  Yusuke Suzuki  <ysuz...@apple.com>
 
+        [JSC] ScopedArgumentsTable should handle OOM in tolerant manner
+        https://bugs.webkit.org/show_bug.cgi?id=210126
+
+        Reviewed by Mark Lam.
+
+        * stress/scoped-arguments-table-should-be-tolerant-for-oom.js: Added.
+        (canThrow):
+        (bar):
+        (get bar):
+        (foo):
+        (i.canThrow):
+
+2020-04-07  Yusuke Suzuki  <ysuz...@apple.com>
+
         [JSC] Inlined IC should get right JSGlobalObject
         https://bugs.webkit.org/show_bug.cgi?id=210092
 

Added: trunk/JSTests/stress/scoped-arguments-table-should-be-tolerant-for-oom.js (0 => 259646)


--- trunk/JSTests/stress/scoped-arguments-table-should-be-tolerant-for-oom.js	                        (rev 0)
+++ trunk/JSTests/stress/scoped-arguments-table-should-be-tolerant-for-oom.js	2020-04-07 18:04:57 UTC (rev 259646)
@@ -0,0 +1,43 @@
+//@ skip if $memoryLimited
+//@ slow!
+
+function canThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (errorThrown && String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+const a0 = [];
+a0.__proto__ = {};
+a0.length = 2**26;
+Object.defineProperty(a0, 0, {get: bar});
+
+function bar() {
+    new Uint8Array(a0);
+}
+
+new Promise(bar);
+
+try {
+    for (let i=0; i<10000; i++) {
+        new Uint8Array(10000);
+    }
+} catch {}
+
+function foo(x, y, z) {
+    delete arguments[0];
+    with (Object) {}
+}
+
+for (let i=0; i<10000; i++) {
+    canThrow(() => {
+        foo(0);
+    }, `Error: Out of memory`);
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (259645 => 259646)


--- trunk/Source/_javascript_Core/ChangeLog	2020-04-07 17:39:24 UTC (rev 259645)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-04-07 18:04:57 UTC (rev 259646)
@@ -1,5 +1,30 @@
 2020-04-07  Yusuke Suzuki  <ysuz...@apple.com>
 
+        [JSC] ScopedArgumentsTable should handle OOM in tolerant manner
+        https://bugs.webkit.org/show_bug.cgi?id=210126
+
+        Reviewed by Mark Lam.
+
+        This patch makes ScopedArgumentsTable allocations OOM tolerant to throw OOM error when allocation fails.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        * runtime/CachedTypes.cpp:
+        (JSC::CachedScopedArgumentsTable::decode const):
+        * runtime/ScopedArguments.cpp:
+        (JSC::ScopedArguments::unmapArgument):
+        * runtime/ScopedArgumentsTable.cpp:
+        (JSC::ScopedArgumentsTable::tryClone):
+        (JSC::ScopedArgumentsTable::trySetLength):
+        (JSC::ScopedArgumentsTable::trySet):
+        (JSC::ScopedArgumentsTable::clone): Deleted.
+        (JSC::ScopedArgumentsTable::setLength): Deleted.
+        (JSC::ScopedArgumentsTable::set): Deleted.
+        * runtime/ScopedArgumentsTable.h:
+        * runtime/SymbolTable.h:
+
+2020-04-07  Yusuke Suzuki  <ysuz...@apple.com>
+
         [JSC] JSWrapperObject should use JSInternalFieldObjectImpl
         https://bugs.webkit.org/show_bug.cgi?id=210019
 

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (259645 => 259646)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2020-04-07 17:39:24 UTC (rev 259645)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2020-04-07 18:04:57 UTC (rev 259646)
@@ -505,7 +505,11 @@
             // way we lift the value into the scope.
             for (unsigned i = 0; i < parameters.size(); ++i) {
                 ScopeOffset offset = functionSymbolTable->takeNextScopeOffset(NoLockingNecessary);
-                functionSymbolTable->setArgumentOffset(vm, i, offset);
+                bool success = functionSymbolTable->trySetArgumentOffset(vm, i, offset);
+                if (UNLIKELY(!success)) {
+                    m_outOfMemoryDuringConstruction = true;
+                    return;
+                }
                 if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first)) {
                     VarOffset varOffset(offset);
                     SymbolTableEntry entry(varOffset);

Modified: trunk/Source/_javascript_Core/runtime/CachedTypes.cpp (259645 => 259646)


--- trunk/Source/_javascript_Core/runtime/CachedTypes.cpp	2020-04-07 17:39:24 UTC (rev 259645)
+++ trunk/Source/_javascript_Core/runtime/CachedTypes.cpp	2020-04-07 18:04:57 UTC (rev 259646)
@@ -1121,7 +1121,8 @@
 
     ScopedArgumentsTable* decode(Decoder& decoder) const
     {
-        ScopedArgumentsTable* scopedArgumentsTable = ScopedArgumentsTable::create(decoder.vm(), m_length);
+        ScopedArgumentsTable* scopedArgumentsTable = ScopedArgumentsTable::tryCreate(decoder.vm(), m_length);
+        RELEASE_ASSERT(scopedArgumentsTable); // We crash here. This is unlikely to continue execution if we hit this condition when decoding UnlinkedCodeBlock.
         m_arguments.decode(decoder, scopedArgumentsTable->m_arguments.get(m_length), m_length);
         return scopedArgumentsTable;
     }

Modified: trunk/Source/_javascript_Core/runtime/ScopedArguments.cpp (259645 => 259646)


--- trunk/Source/_javascript_Core/runtime/ScopedArguments.cpp	2020-04-07 17:39:24 UTC (rev 259645)
+++ trunk/Source/_javascript_Core/runtime/ScopedArguments.cpp	2020-04-07 18:04:57 UTC (rev 259646)
@@ -143,11 +143,17 @@
 void ScopedArguments::unmapArgument(JSGlobalObject* globalObject, uint32_t i)
 {
     VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
     ASSERT_WITH_SECURITY_IMPLICATION(i < m_totalLength);
     unsigned namedLength = m_table->length();
-    if (i < namedLength)
-        m_table.set(vm, this, m_table->set(vm, i, ScopeOffset()));
-    else
+    if (i < namedLength) {
+        auto* maybeCloned = m_table->trySet(vm, i, ScopeOffset());
+        if (UNLIKELY(!maybeCloned)) {
+            throwOutOfMemoryError(globalObject, scope);
+            return;
+        }
+        m_table.set(vm, this, maybeCloned);
+    } else
         storage()[i - namedLength].clear();
 }
 

Modified: trunk/Source/_javascript_Core/runtime/ScopedArgumentsTable.cpp (259645 => 259646)


--- trunk/Source/_javascript_Core/runtime/ScopedArgumentsTable.cpp	2020-04-07 17:39:24 UTC (rev 259645)
+++ trunk/Source/_javascript_Core/runtime/ScopedArgumentsTable.cpp	2020-04-07 18:04:57 UTC (rev 259646)
@@ -56,14 +56,6 @@
     return result;
 }
 
-ScopedArgumentsTable* ScopedArgumentsTable::create(VM& vm, uint32_t length)
-{
-    ScopedArgumentsTable* result = create(vm);
-    result->m_length = length;
-    result->m_arguments = ArgumentsPtr::create(length);
-    return result;
-}
-
 ScopedArgumentsTable* ScopedArgumentsTable::tryCreate(VM& vm, uint32_t length)
 {
     void* buffer = tryAllocateCell<ScopedArgumentsTable>(vm.heap);
@@ -79,18 +71,22 @@
     return result;
 }
 
-ScopedArgumentsTable* ScopedArgumentsTable::clone(VM& vm)
+ScopedArgumentsTable* ScopedArgumentsTable::tryClone(VM& vm)
 {
-    ScopedArgumentsTable* result = create(vm, m_length);
+    ScopedArgumentsTable* result = tryCreate(vm, m_length);
+    if (UNLIKELY(!result))
+        return nullptr;
     for (unsigned i = m_length; i--;)
         result->at(i) = this->at(i);
     return result;
 }
 
-ScopedArgumentsTable* ScopedArgumentsTable::setLength(VM& vm, uint32_t newLength)
+ScopedArgumentsTable* ScopedArgumentsTable::trySetLength(VM& vm, uint32_t newLength)
 {
     if (LIKELY(!m_locked)) {
-        ArgumentsPtr newArguments = ArgumentsPtr::create(newLength, newLength);
+        ArgumentsPtr newArguments = ArgumentsPtr::tryCreate(newLength, newLength);
+        if (UNLIKELY(!newArguments))
+            return nullptr;
         for (unsigned i = std::min(m_length, newLength); i--;)
             newArguments.at(i, newLength) = this->at(i);
         m_length = newLength;
@@ -98,7 +94,9 @@
         return this;
     }
     
-    ScopedArgumentsTable* result = create(vm, newLength);
+    ScopedArgumentsTable* result = tryCreate(vm, newLength);
+    if (UNLIKELY(!result))
+        return nullptr;
     for (unsigned i = std::min(m_length, newLength); i--;)
         result->at(i) = this->at(i);
     return result;
@@ -106,12 +104,14 @@
 
 static_assert(std::is_trivially_destructible<ScopeOffset>::value, "");
 
-ScopedArgumentsTable* ScopedArgumentsTable::set(VM& vm, uint32_t i, ScopeOffset value)
+ScopedArgumentsTable* ScopedArgumentsTable::trySet(VM& vm, uint32_t i, ScopeOffset value)
 {
     ScopedArgumentsTable* result;
-    if (UNLIKELY(m_locked))
-        result = clone(vm);
-    else
+    if (UNLIKELY(m_locked)) {
+        result = tryClone(vm);
+        if (UNLIKELY(!result))
+            return nullptr;
+    } else
         result = this;
     result->at(i) = value;
     return result;

Modified: trunk/Source/_javascript_Core/runtime/ScopedArgumentsTable.h (259645 => 259646)


--- trunk/Source/_javascript_Core/runtime/ScopedArgumentsTable.h	2020-04-07 17:39:24 UTC (rev 259645)
+++ trunk/Source/_javascript_Core/runtime/ScopedArgumentsTable.h	2020-04-07 18:04:57 UTC (rev 259646)
@@ -59,15 +59,12 @@
 
 public:
     static ScopedArgumentsTable* create(VM&);
-    static ScopedArgumentsTable* create(VM&, uint32_t length);
     static ScopedArgumentsTable* tryCreate(VM&, uint32_t length);
 
     static void destroy(JSCell*);
 
-    ScopedArgumentsTable* clone(VM&);
-    
     uint32_t length() const { return m_length; }
-    ScopedArgumentsTable* setLength(VM&, uint32_t newLength);
+    ScopedArgumentsTable* trySetLength(VM&, uint32_t newLength);
     
     ScopeOffset get(uint32_t i) const { return at(i); }
     
@@ -76,7 +73,7 @@
         m_locked = true;
     }
     
-    ScopedArgumentsTable* set(VM&, uint32_t index, ScopeOffset);
+    ScopedArgumentsTable* trySet(VM&, uint32_t index, ScopeOffset);
     
     DECLARE_INFO;
     
@@ -88,6 +85,8 @@
     typedef CagedUniquePtr<Gigacage::Primitive, ScopeOffset> ArgumentsPtr;
 
 private:
+    ScopedArgumentsTable* tryClone(VM&);
+
     ScopeOffset& at(uint32_t i) const
     {
         ASSERT_WITH_SECURITY_IMPLICATION(i < m_length);

Modified: trunk/Source/_javascript_Core/runtime/SymbolTable.h (259645 => 259646)


--- trunk/Source/_javascript_Core/runtime/SymbolTable.h	2020-04-07 17:39:24 UTC (rev 259645)
+++ trunk/Source/_javascript_Core/runtime/SymbolTable.h	2020-04-07 18:04:57 UTC (rev 259646)
@@ -642,8 +642,12 @@
             if (UNLIKELY(!table))
                 return false;
             m_arguments.set(vm, this, table);
-        } else
-            m_arguments.set(vm, this, m_arguments->setLength(vm, length));
+        } else {
+            ScopedArgumentsTable* table = m_arguments->trySetLength(vm, length);
+            if (UNLIKELY(!table))
+                return false;
+            m_arguments.set(vm, this, table);
+        }
         return true;
     }
 
@@ -653,10 +657,14 @@
         return m_arguments->get(i);
     }
     
-    void setArgumentOffset(VM& vm, uint32_t i, ScopeOffset offset)
+    bool trySetArgumentOffset(VM& vm, uint32_t i, ScopeOffset offset)
     {
         ASSERT_WITH_SECURITY_IMPLICATION(m_arguments);
-        m_arguments.set(vm, this, m_arguments->set(vm, i, offset));
+        auto* maybeCloned = m_arguments->trySet(vm, i, offset);
+        if (!maybeCloned)
+            return false;
+        m_arguments.set(vm, this, maybeCloned);
+        return true;
     }
     
     ScopedArgumentsTable* arguments() const
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to