Title: [256087] trunk/Source/_javascript_Core
Revision
256087
Author
[email protected]
Date
2020-02-07 22:08:33 -0800 (Fri, 07 Feb 2020)

Log Message

Throw OutOfMemory exception instead of crashing if DirectArguments/ScopedArguments can't be created
https://bugs.webkit.org/show_bug.cgi?id=207423

Reviewed by Mark Lam.

AllocationFailureMode::Assert is problematic because fuzzers keep producing spurious error reports when they generate code that tries allocating infinite amount of memory.
The right approach is to use AllocationFailureMode::ReturnNull, and throw a JS exception upon receiving null.

In this patch I fixed two functions that were using AllocationFailureMode::Assert:
    DirectArguments::DirectArguments::overrideThings
    GenericArguments<Type>::initModifiedArgumentsDescriptor

No test added, because the only test we have is highly non-deterministic/flaky (only triggers about 10 to 20% of the time even before the fix).

* runtime/DirectArguments.h:
* runtime/GenericArguments.h:
* runtime/GenericArgumentsInlines.h:
(JSC::GenericArguments<Type>::deletePropertyByIndex):
(JSC::GenericArguments<Type>::defineOwnProperty):
(JSC::GenericArguments<Type>::initModifiedArgumentsDescriptor):
(JSC::GenericArguments<Type>::initModifiedArgumentsDescriptorIfNecessary):
(JSC::GenericArguments<Type>::setModifiedArgumentDescriptor):
* runtime/ScopedArguments.h:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (256086 => 256087)


--- trunk/Source/_javascript_Core/ChangeLog	2020-02-08 04:21:21 UTC (rev 256086)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-02-08 06:08:33 UTC (rev 256087)
@@ -1,3 +1,29 @@
+2020-02-07  Robin Morisset  <[email protected]>
+
+        Throw OutOfMemory exception instead of crashing if DirectArguments/ScopedArguments can't be created
+        https://bugs.webkit.org/show_bug.cgi?id=207423
+
+        Reviewed by Mark Lam.
+
+        AllocationFailureMode::Assert is problematic because fuzzers keep producing spurious error reports when they generate code that tries allocating infinite amount of memory.
+        The right approach is to use AllocationFailureMode::ReturnNull, and throw a JS exception upon receiving null.
+
+        In this patch I fixed two functions that were using AllocationFailureMode::Assert:
+            DirectArguments::DirectArguments::overrideThings
+            GenericArguments<Type>::initModifiedArgumentsDescriptor
+
+        No test added, because the only test we have is highly non-deterministic/flaky (only triggers about 10 to 20% of the time even before the fix).
+
+        * runtime/DirectArguments.h:
+        * runtime/GenericArguments.h:
+        * runtime/GenericArgumentsInlines.h:
+        (JSC::GenericArguments<Type>::deletePropertyByIndex):
+        (JSC::GenericArguments<Type>::defineOwnProperty):
+        (JSC::GenericArguments<Type>::initModifiedArgumentsDescriptor):
+        (JSC::GenericArguments<Type>::initModifiedArgumentsDescriptorIfNecessary):
+        (JSC::GenericArguments<Type>::setModifiedArgumentDescriptor):
+        * runtime/ScopedArguments.h:
+
 2020-02-07  Ryan Haddad  <[email protected]>
 
         Unreviewed, rolling out r256051.

Modified: trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp (256086 => 256087)


--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2020-02-08 04:21:21 UTC (rev 256086)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2020-02-08 06:08:33 UTC (rev 256087)
@@ -987,6 +987,7 @@
         CHECK_EXCEPTION();
         couldDelete = baseObject->methodTable(vm)->deleteProperty(baseObject, globalObject, property);
     }
+    CHECK_EXCEPTION();
     
     if (!couldDelete && codeBlock->isStrictMode())
         THROW(createTypeError(globalObject, UnableToDeletePropertyError));

Modified: trunk/Source/_javascript_Core/runtime/DirectArguments.cpp (256086 => 256087)


--- trunk/Source/_javascript_Core/runtime/DirectArguments.cpp	2020-02-08 04:21:21 UTC (rev 256086)
+++ trunk/Source/_javascript_Core/runtime/DirectArguments.cpp	2020-02-08 06:08:33 UTC (rev 256087)
@@ -110,15 +110,22 @@
     return Structure::create(vm, globalObject, prototype, TypeInfo(DirectArgumentsType, StructureFlags), info());
 }
 
-void DirectArguments::overrideThings(VM& vm)
+void DirectArguments::overrideThings(JSGlobalObject* globalObject)
 {
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     RELEASE_ASSERT(!m_mappedArguments);
     
     putDirect(vm, vm.propertyNames->length, jsNumber(m_length), static_cast<unsigned>(PropertyAttribute::DontEnum));
     putDirect(vm, vm.propertyNames->callee, m_callee.get(), static_cast<unsigned>(PropertyAttribute::DontEnum));
-    putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject(vm)->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
+    putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
     
-    void* backingStore = vm.gigacageAuxiliarySpace(m_mappedArguments.kind).allocateNonVirtual(vm, mappedArgumentsSize(), nullptr, AllocationFailureMode::Assert);
+    void* backingStore = vm.gigacageAuxiliarySpace(m_mappedArguments.kind).allocateNonVirtual(vm, mappedArgumentsSize(), nullptr, AllocationFailureMode::ReturnNull);
+    if (UNLIKELY(!backingStore)) {
+        throwOutOfMemoryError(globalObject, scope);
+        return;
+    }
     bool* overrides = static_cast<bool*>(backingStore);
     m_mappedArguments.set(vm, this, overrides, internalLength());
     for (unsigned i = internalLength(); i--;)
@@ -125,15 +132,20 @@
         overrides[i] = false;
 }
 
-void DirectArguments::overrideThingsIfNecessary(VM& vm)
+void DirectArguments::overrideThingsIfNecessary(JSGlobalObject* globalObject)
 {
     if (!m_mappedArguments)
-        overrideThings(vm);
+        overrideThings(globalObject);
 }
 
-void DirectArguments::unmapArgument(VM& vm, unsigned index)
+void DirectArguments::unmapArgument(JSGlobalObject* globalObject, unsigned index)
 {
-    overrideThingsIfNecessary(vm);
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    overrideThingsIfNecessary(globalObject);
+    RETURN_IF_EXCEPTION(scope, void());
+
     m_mappedArguments.at(index, internalLength()) = true;
 }
 

Modified: trunk/Source/_javascript_Core/runtime/DirectArguments.h (256086 => 256087)


--- trunk/Source/_javascript_Core/runtime/DirectArguments.h	2020-02-08 04:21:21 UTC (rev 256086)
+++ trunk/Source/_javascript_Core/runtime/DirectArguments.h	2020-02-08 06:08:33 UTC (rev 256087)
@@ -125,18 +125,18 @@
     
     // Methods intended for use by the GenericArguments mixin.
     bool overrodeThings() const { return !!m_mappedArguments; }
-    void overrideThings(VM&);
-    void overrideThingsIfNecessary(VM&);
-    void unmapArgument(VM&, unsigned index);
+    void overrideThings(JSGlobalObject*);
+    void overrideThingsIfNecessary(JSGlobalObject*);
+    void unmapArgument(JSGlobalObject*, unsigned index);
 
-    void initModifiedArgumentsDescriptorIfNecessary(VM& vm)
+    void initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject* globalObject)
     {
-        GenericArguments<DirectArguments>::initModifiedArgumentsDescriptorIfNecessary(vm, m_length);
+        GenericArguments<DirectArguments>::initModifiedArgumentsDescriptorIfNecessary(globalObject, m_length);
     }
 
-    void setModifiedArgumentDescriptor(VM& vm, unsigned index)
+    void setModifiedArgumentDescriptor(JSGlobalObject* globalObject, unsigned index)
     {
-        GenericArguments<DirectArguments>::setModifiedArgumentDescriptor(vm, index, m_length);
+        GenericArguments<DirectArguments>::setModifiedArgumentDescriptor(globalObject, index, m_length);
     }
 
     bool isModifiedArgumentDescriptor(unsigned index)

Modified: trunk/Source/_javascript_Core/runtime/GenericArguments.h (256086 => 256087)


--- trunk/Source/_javascript_Core/runtime/GenericArguments.h	2020-02-08 04:21:21 UTC (rev 256086)
+++ trunk/Source/_javascript_Core/runtime/GenericArguments.h	2020-02-08 06:08:33 UTC (rev 256087)
@@ -54,9 +54,9 @@
     static bool deletePropertyByIndex(JSCell*, JSGlobalObject*, unsigned propertyName);
     static bool defineOwnProperty(JSObject*, JSGlobalObject*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
     
-    void initModifiedArgumentsDescriptor(VM&, unsigned length);
-    void initModifiedArgumentsDescriptorIfNecessary(VM&, unsigned length);
-    void setModifiedArgumentDescriptor(VM&, unsigned index, unsigned length);
+    void initModifiedArgumentsDescriptor(JSGlobalObject*, unsigned length);
+    void initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject*, unsigned length);
+    void setModifiedArgumentDescriptor(JSGlobalObject*, unsigned index, unsigned length);
     bool isModifiedArgumentDescriptor(unsigned index, unsigned length);
 
     void copyToArguments(JSGlobalObject*, JSValue* firstElementDest, unsigned offset, unsigned length);

Modified: trunk/Source/_javascript_Core/runtime/GenericArgumentsInlines.h (256086 => 256087)


--- trunk/Source/_javascript_Core/runtime/GenericArgumentsInlines.h	2020-02-08 04:21:21 UTC (rev 256086)
+++ trunk/Source/_javascript_Core/runtime/GenericArgumentsInlines.h	2020-02-08 06:08:33 UTC (rev 256087)
@@ -117,14 +117,16 @@
 {
     Type* thisObject = jsCast<Type*>(cell);
     VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
     
     if (!thisObject->overrodeThings()
         && (ident == vm.propertyNames->length
             || ident == vm.propertyNames->callee
             || ident == vm.propertyNames->iteratorSymbol)) {
-        thisObject->overrideThings(vm);
+        thisObject->overrideThings(globalObject);
+        RETURN_IF_EXCEPTION(scope, false);
         PutPropertySlot dummy = slot; // This put is not cacheable, so we shadow the slot that was given to us.
-        return Base::put(thisObject, globalObject, ident, value, dummy);
+        RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, ident, value, dummy));
     }
 
     // https://tc39.github.io/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver
@@ -138,7 +140,7 @@
         return true;
     }
     
-    return Base::put(thisObject, globalObject, ident, value, slot);
+    RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, ident, value, slot));
 }
 
 template<typename Type>
@@ -160,25 +162,30 @@
 {
     Type* thisObject = jsCast<Type*>(cell);
     VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
     
     if (!thisObject->overrodeThings()
         && (ident == vm.propertyNames->length
             || ident == vm.propertyNames->callee
-            || ident == vm.propertyNames->iteratorSymbol))
-        thisObject->overrideThings(vm);
-    
+            || ident == vm.propertyNames->iteratorSymbol)) {
+        thisObject->overrideThings(globalObject);
+        RETURN_IF_EXCEPTION(scope, false);
+    }
+
     if (Optional<uint32_t> index = parseIndex(ident))
-        return GenericArguments<Type>::deletePropertyByIndex(thisObject, globalObject, *index);
+        RELEASE_AND_RETURN(scope, GenericArguments<Type>::deletePropertyByIndex(thisObject, globalObject, *index));
 
-    return Base::deleteProperty(thisObject, globalObject, ident);
+    RELEASE_AND_RETURN(scope, Base::deleteProperty(thisObject, globalObject, ident));
 }
 
 template<typename Type>
 bool GenericArguments<Type>::deletePropertyByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned index)
 {
-    Type* thisObject = jsCast<Type*>(cell);
     VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
+    Type* thisObject = jsCast<Type*>(cell);
+
     bool propertyMightBeInJSObjectStorage = thisObject->isModifiedArgumentDescriptor(index) || !thisObject->isMappedArgument(index);
     bool deletedProperty = true;
     if (propertyMightBeInJSObjectStorage)
@@ -188,9 +195,11 @@
         // Deleting an indexed property unconditionally unmaps it.
         if (thisObject->isMappedArgument(index)) {
             // We need to check that the property was mapped so we don't write to random memory.
-            thisObject->unmapArgument(vm, index);
+            thisObject->unmapArgument(globalObject, index);
+            RETURN_IF_EXCEPTION(scope, true);
         }
-        thisObject->setModifiedArgumentDescriptor(vm, index);
+        thisObject->setModifiedArgumentDescriptor(globalObject, index);
+        RETURN_IF_EXCEPTION(scope, true);
     }
 
     return deletedProperty;
@@ -205,9 +214,10 @@
     
     if (ident == vm.propertyNames->length
         || ident == vm.propertyNames->callee
-        || ident == vm.propertyNames->iteratorSymbol)
-        thisObject->overrideThingsIfNecessary(vm);
-    else {
+        || ident == vm.propertyNames->iteratorSymbol) {
+        thisObject->overrideThingsIfNecessary(globalObject);
+        RETURN_IF_EXCEPTION(scope, false);
+    } else {
         Optional<uint32_t> optionalIndex = parseIndex(ident);
         if (optionalIndex) {
             uint32_t index = optionalIndex.value();
@@ -230,7 +240,8 @@
                     object->putDirectMayBeIndex(globalObject, ident, value);
                     scope.assertNoException();
 
-                    thisObject->setModifiedArgumentDescriptor(vm, index);
+                    thisObject->setModifiedArgumentDescriptor(globalObject, index);
+                    RETURN_IF_EXCEPTION(scope, false);
                 }
             }
             
@@ -246,8 +257,10 @@
                         object->putDirectMayBeIndex(globalObject, ident, value);
                         scope.assertNoException();
                     }
-                    thisObject->unmapArgument(vm, index);
-                    thisObject->setModifiedArgumentDescriptor(vm, index);
+                    thisObject->unmapArgument(globalObject, index);
+                    RETURN_IF_EXCEPTION(scope, false);
+                    thisObject->setModifiedArgumentDescriptor(globalObject, index);
+                    RETURN_IF_EXCEPTION(scope, false);
                 }
             }
         }
@@ -258,12 +271,19 @@
 }
 
 template<typename Type>
-void GenericArguments<Type>::initModifiedArgumentsDescriptor(VM& vm, unsigned argsLength)
+void GenericArguments<Type>::initModifiedArgumentsDescriptor(JSGlobalObject* globalObject, unsigned argsLength)
 {
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     RELEASE_ASSERT(!m_modifiedArgumentsDescriptor);
 
     if (argsLength) {
-        void* backingStore = vm.gigacageAuxiliarySpace(m_modifiedArgumentsDescriptor.kind).allocateNonVirtual(vm, WTF::roundUpToMultipleOf<8>(argsLength), nullptr, AllocationFailureMode::Assert);
+        void* backingStore = vm.gigacageAuxiliarySpace(m_modifiedArgumentsDescriptor.kind).allocateNonVirtual(vm, WTF::roundUpToMultipleOf<8>(argsLength), nullptr, AllocationFailureMode::ReturnNull);
+        if (UNLIKELY(!backingStore)) {
+            throwOutOfMemoryError(globalObject, scope);
+            return;
+        }
         bool* modifiedArguments = static_cast<bool*>(backingStore);
         m_modifiedArgumentsDescriptor.set(vm, this, modifiedArguments, argsLength);
         for (unsigned i = argsLength; i--;)
@@ -272,16 +292,20 @@
 }
 
 template<typename Type>
-void GenericArguments<Type>::initModifiedArgumentsDescriptorIfNecessary(VM& vm, unsigned argsLength)
+void GenericArguments<Type>::initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject* globalObject, unsigned argsLength)
 {
     if (!m_modifiedArgumentsDescriptor)
-        initModifiedArgumentsDescriptor(vm, argsLength);
+        initModifiedArgumentsDescriptor(globalObject, argsLength);
 }
 
 template<typename Type>
-void GenericArguments<Type>::setModifiedArgumentDescriptor(VM& vm, unsigned index, unsigned length)
+void GenericArguments<Type>::setModifiedArgumentDescriptor(JSGlobalObject* globalObject, unsigned index, unsigned length)
 {
-    initModifiedArgumentsDescriptorIfNecessary(vm, length);
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    initModifiedArgumentsDescriptorIfNecessary(globalObject, length);
+    RETURN_IF_EXCEPTION(scope, void());
     if (index < length)
         m_modifiedArgumentsDescriptor.at(index, length) = true;
 }

Modified: trunk/Source/_javascript_Core/runtime/ScopedArguments.cpp (256086 => 256087)


--- trunk/Source/_javascript_Core/runtime/ScopedArguments.cpp	2020-02-08 04:21:21 UTC (rev 256086)
+++ trunk/Source/_javascript_Core/runtime/ScopedArguments.cpp	2020-02-08 06:08:33 UTC (rev 256087)
@@ -121,25 +121,28 @@
     return Structure::create(vm, globalObject, prototype, TypeInfo(ScopedArgumentsType, StructureFlags), info());
 }
 
-void ScopedArguments::overrideThings(VM& vm)
+void ScopedArguments::overrideThings(JSGlobalObject* globalObject)
 {
+    VM& vm = globalObject->vm();
+
     RELEASE_ASSERT(!m_overrodeThings);
     
     putDirect(vm, vm.propertyNames->length, jsNumber(m_table->length()), static_cast<unsigned>(PropertyAttribute::DontEnum));
     putDirect(vm, vm.propertyNames->callee, m_callee.get(), static_cast<unsigned>(PropertyAttribute::DontEnum));
-    putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject(vm)->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
+    putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
     
     m_overrodeThings = true;
 }
 
-void ScopedArguments::overrideThingsIfNecessary(VM& vm)
+void ScopedArguments::overrideThingsIfNecessary(JSGlobalObject* globalObject)
 {
     if (!m_overrodeThings)
-        overrideThings(vm);
+        overrideThings(globalObject);
 }
 
-void ScopedArguments::unmapArgument(VM& vm, uint32_t i)
+void ScopedArguments::unmapArgument(JSGlobalObject* globalObject, uint32_t i)
 {
+    VM& vm = globalObject->vm();
     ASSERT_WITH_SECURITY_IMPLICATION(i < m_totalLength);
     unsigned namedLength = m_table->length();
     if (i < namedLength)

Modified: trunk/Source/_javascript_Core/runtime/ScopedArguments.h (256086 => 256087)


--- trunk/Source/_javascript_Core/runtime/ScopedArguments.h	2020-02-08 04:21:21 UTC (rev 256086)
+++ trunk/Source/_javascript_Core/runtime/ScopedArguments.h	2020-02-08 06:08:33 UTC (rev 256087)
@@ -123,18 +123,18 @@
     }
 
     bool overrodeThings() const { return m_overrodeThings; }
-    void overrideThings(VM&);
-    void overrideThingsIfNecessary(VM&);
-    void unmapArgument(VM&, uint32_t index);
+    void overrideThings(JSGlobalObject*);
+    void overrideThingsIfNecessary(JSGlobalObject*);
+    void unmapArgument(JSGlobalObject*, uint32_t index);
     
-    void initModifiedArgumentsDescriptorIfNecessary(VM& vm)
+    void initModifiedArgumentsDescriptorIfNecessary(JSGlobalObject* globalObject)
     {
-        GenericArguments<ScopedArguments>::initModifiedArgumentsDescriptorIfNecessary(vm, m_table->length());
+        GenericArguments<ScopedArguments>::initModifiedArgumentsDescriptorIfNecessary(globalObject, m_table->length());
     }
 
-    void setModifiedArgumentDescriptor(VM& vm, unsigned index)
+    void setModifiedArgumentDescriptor(JSGlobalObject* globalObject, unsigned index)
     {
-        GenericArguments<ScopedArguments>::setModifiedArgumentDescriptor(vm, index, m_table->length());
+        GenericArguments<ScopedArguments>::setModifiedArgumentDescriptor(globalObject, index, m_table->length());
     }
 
     bool isModifiedArgumentDescriptor(unsigned index)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to