Title: [201495] trunk/Source/_javascript_Core
Revision
201495
Author
[email protected]
Date
2016-05-29 12:01:36 -0700 (Sun, 29 May 2016)

Log Message

Stack overflow crashes with deep or cyclic proxy prototype chains
https://bugs.webkit.org/show_bug.cgi?id=157087

Reviewed by Filip Pizlo and Mark Lam.

Because a Proxy can call back into the JS runtime in arbitrary
ways, we may have effectively cyclic prototype chains and property lookups
by using a Proxy. We may also have arbitrarily long Proxy chains
where we call into a C frame for each link in the Proxy chain.
This means that every Proxy hook must be aware that it can stack overflow.
Before, only certain hooks were aware of this fact. That was a bug,
all hooks must assume they can stack overflow.

Also, because we may have effectively cyclic prototype chains, we
compile ProxyObject.cpp with -fno-optimize-sibling-calls. This prevents
tail call optimization from happening on any of the calls from
ProxyObject.cpp. We do this because we rely on the machine stack
growing for throwing a stack overflow error. It's better for developers
to be able to see a stack overflow error than to have their program
infinite loop because the compiler performed TCO.

This patch also fixes a couple call sites of various methods
where we didn't check for an exception.

* CMakeLists.txt:
* _javascript_Core.xcodeproj/project.pbxproj:
* interpreter/Interpreter.cpp:
(JSC::sizeOfVarargs):
* runtime/InternalFunction.cpp:
(JSC::InternalFunction::createSubclassStructure):
* runtime/JSArray.h:
(JSC::getLength):
* runtime/ObjectPrototype.cpp:
(JSC::objectProtoFuncToString):
* runtime/ProxyObject.cpp:
(JSC::performProxyGet):
(JSC::ProxyObject::performInternalMethodGetOwnProperty):
(JSC::ProxyObject::performHasProperty):
(JSC::ProxyObject::getOwnPropertySlotCommon):
(JSC::ProxyObject::performPut):
(JSC::performProxyCall):
(JSC::performProxyConstruct):
(JSC::ProxyObject::performDelete):
(JSC::ProxyObject::performPreventExtensions):
(JSC::ProxyObject::performIsExtensible):
(JSC::ProxyObject::performDefineOwnProperty):
(JSC::ProxyObject::performGetOwnPropertyNames):
(JSC::ProxyObject::getOwnPropertyNames):
(JSC::ProxyObject::getPropertyNames):
(JSC::ProxyObject::getOwnNonIndexPropertyNames):
(JSC::ProxyObject::performSetPrototype):
(JSC::ProxyObject::performGetPrototype):
* runtime/ProxyObject.h:
(JSC::ProxyObject::create):
* tests/stress/proxy-stack-overflow-exceptions.js: Added.
(shouldThrowStackOverflow):
(const.emptyFunction):
(makeLongProxyChain):
(shouldThrowStackOverflow.longProxyChain):
(shouldThrowStackOverflow.effecivelyCyclicProxyProtoChain1):
(shouldThrowStackOverflow.effecivelyCyclicProxyProtoChain2):
(shouldThrowStackOverflow.effecivelyCyclicProxyProtoChain3):
(shouldThrowStackOverflow.longProxyChainBind):
(shouldThrowStackOverflow.longProxyChainPropertyAccess):
(shouldThrowStackOverflow.longProxyChainReflectConstruct):
(shouldThrowStackOverflow.longProxyChainReflectSet):
(shouldThrowStackOverflow.longProxyChainReflectOwnKeys):
(shouldThrowStackOverflow.longProxyChainGetPrototypeOf):
(shouldThrowStackOverflow.longProxyChainSetPrototypeOf):
(shouldThrowStackOverflow.longProxyChainGetOwnPropertyDescriptor):
(shouldThrowStackOverflow.longProxyChainDefineProperty):
(shouldThrowStackOverflow.longProxyChainIsExtensible):
(shouldThrowStackOverflow.longProxyChainPreventExtensions):
(shouldThrowStackOverflow.longProxyChainDeleteProperty):
(shouldThrowStackOverflow.longProxyChainWithScope):
(shouldThrowStackOverflow.longProxyChainWithScope2):
(shouldThrowStackOverflow.longProxyChainWithScope3):
(shouldThrowStackOverflow.longProxyChainArrayPrototypePush):
(shouldThrowStackOverflow.longProxyChainWithScope4):
(shouldThrowStackOverflow.longProxyChainCall):
(shouldThrowStackOverflow.longProxyChainConstruct):
(shouldThrowStackOverflow.longProxyChainHas):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/CMakeLists.txt (201494 => 201495)


--- trunk/Source/_javascript_Core/CMakeLists.txt	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/CMakeLists.txt	2016-05-29 19:01:36 UTC (rev 201495)
@@ -845,6 +845,9 @@
     yarr/YarrSyntaxChecker.cpp
 )
 
+# Extra flags for compile sources can go here.
+set_source_files_properties(runtime/ProxyObject.cpp PROPERTIES COMPILE_FLAGS -fno-optimize-sibling-calls)
+
 set(_javascript_Core_OBJECT_LUT_SOURCES
     runtime/ArrayConstructor.cpp
     runtime/ArrayIteratorPrototype.cpp

Modified: trunk/Source/_javascript_Core/ChangeLog (201494 => 201495)


--- trunk/Source/_javascript_Core/ChangeLog	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-05-29 19:01:36 UTC (rev 201495)
@@ -1,3 +1,88 @@
+2016-05-29  Saam barati  <[email protected]>
+
+        Stack overflow crashes with deep or cyclic proxy prototype chains
+        https://bugs.webkit.org/show_bug.cgi?id=157087
+
+        Reviewed by Filip Pizlo and Mark Lam.
+
+        Because a Proxy can call back into the JS runtime in arbitrary
+        ways, we may have effectively cyclic prototype chains and property lookups
+        by using a Proxy. We may also have arbitrarily long Proxy chains
+        where we call into a C frame for each link in the Proxy chain.
+        This means that every Proxy hook must be aware that it can stack overflow.
+        Before, only certain hooks were aware of this fact. That was a bug,
+        all hooks must assume they can stack overflow.
+
+        Also, because we may have effectively cyclic prototype chains, we
+        compile ProxyObject.cpp with -fno-optimize-sibling-calls. This prevents
+        tail call optimization from happening on any of the calls from
+        ProxyObject.cpp. We do this because we rely on the machine stack
+        growing for throwing a stack overflow error. It's better for developers
+        to be able to see a stack overflow error than to have their program
+        infinite loop because the compiler performed TCO.
+
+        This patch also fixes a couple call sites of various methods
+        where we didn't check for an exception.
+
+        * CMakeLists.txt:
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * interpreter/Interpreter.cpp:
+        (JSC::sizeOfVarargs):
+        * runtime/InternalFunction.cpp:
+        (JSC::InternalFunction::createSubclassStructure):
+        * runtime/JSArray.h:
+        (JSC::getLength):
+        * runtime/ObjectPrototype.cpp:
+        (JSC::objectProtoFuncToString):
+        * runtime/ProxyObject.cpp:
+        (JSC::performProxyGet):
+        (JSC::ProxyObject::performInternalMethodGetOwnProperty):
+        (JSC::ProxyObject::performHasProperty):
+        (JSC::ProxyObject::getOwnPropertySlotCommon):
+        (JSC::ProxyObject::performPut):
+        (JSC::performProxyCall):
+        (JSC::performProxyConstruct):
+        (JSC::ProxyObject::performDelete):
+        (JSC::ProxyObject::performPreventExtensions):
+        (JSC::ProxyObject::performIsExtensible):
+        (JSC::ProxyObject::performDefineOwnProperty):
+        (JSC::ProxyObject::performGetOwnPropertyNames):
+        (JSC::ProxyObject::getOwnPropertyNames):
+        (JSC::ProxyObject::getPropertyNames):
+        (JSC::ProxyObject::getOwnNonIndexPropertyNames):
+        (JSC::ProxyObject::performSetPrototype):
+        (JSC::ProxyObject::performGetPrototype):
+        * runtime/ProxyObject.h:
+        (JSC::ProxyObject::create):
+        * tests/stress/proxy-stack-overflow-exceptions.js: Added.
+        (shouldThrowStackOverflow):
+        (const.emptyFunction):
+        (makeLongProxyChain):
+        (shouldThrowStackOverflow.longProxyChain):
+        (shouldThrowStackOverflow.effecivelyCyclicProxyProtoChain1):
+        (shouldThrowStackOverflow.effecivelyCyclicProxyProtoChain2):
+        (shouldThrowStackOverflow.effecivelyCyclicProxyProtoChain3):
+        (shouldThrowStackOverflow.longProxyChainBind):
+        (shouldThrowStackOverflow.longProxyChainPropertyAccess):
+        (shouldThrowStackOverflow.longProxyChainReflectConstruct):
+        (shouldThrowStackOverflow.longProxyChainReflectSet):
+        (shouldThrowStackOverflow.longProxyChainReflectOwnKeys):
+        (shouldThrowStackOverflow.longProxyChainGetPrototypeOf):
+        (shouldThrowStackOverflow.longProxyChainSetPrototypeOf):
+        (shouldThrowStackOverflow.longProxyChainGetOwnPropertyDescriptor):
+        (shouldThrowStackOverflow.longProxyChainDefineProperty):
+        (shouldThrowStackOverflow.longProxyChainIsExtensible):
+        (shouldThrowStackOverflow.longProxyChainPreventExtensions):
+        (shouldThrowStackOverflow.longProxyChainDeleteProperty):
+        (shouldThrowStackOverflow.longProxyChainWithScope):
+        (shouldThrowStackOverflow.longProxyChainWithScope2):
+        (shouldThrowStackOverflow.longProxyChainWithScope3):
+        (shouldThrowStackOverflow.longProxyChainArrayPrototypePush):
+        (shouldThrowStackOverflow.longProxyChainWithScope4):
+        (shouldThrowStackOverflow.longProxyChainCall):
+        (shouldThrowStackOverflow.longProxyChainConstruct):
+        (shouldThrowStackOverflow.longProxyChainHas):
+
 2016-05-28  Andreas Kling  <[email protected]>
 
         JSGlobalLexicalEnvironment leaks SegmentedVector due to lack of destructor.

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (201494 => 201495)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-05-29 19:01:36 UTC (rev 201495)
@@ -1288,7 +1288,7 @@
 		799EF7C41C56ED96002B0534 /* B3PCToOriginMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 799EF7C31C56ED96002B0534 /* B3PCToOriginMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		79B00CBC1C6AB07E0088C65D /* ProxyConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79B00CB81C6AB07E0088C65D /* ProxyConstructor.cpp */; };
 		79B00CBD1C6AB07E0088C65D /* ProxyConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 79B00CB91C6AB07E0088C65D /* ProxyConstructor.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		79B00CBE1C6AB07E0088C65D /* ProxyObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79B00CBA1C6AB07E0088C65D /* ProxyObject.cpp */; };
+		79B00CBE1C6AB07E0088C65D /* ProxyObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79B00CBA1C6AB07E0088C65D /* ProxyObject.cpp */; settings = {COMPILER_FLAGS = "-fno-optimize-sibling-calls"; }; };
 		79B00CBF1C6AB07E0088C65D /* ProxyObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 79B00CBB1C6AB07E0088C65D /* ProxyObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		79C4B15D1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B15B1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp */; };
 		79C4B15E1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 79C4B15C1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };

Modified: trunk/Source/_javascript_Core/interpreter/Interpreter.cpp (201494 => 201495)


--- trunk/Source/_javascript_Core/interpreter/Interpreter.cpp	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/interpreter/Interpreter.cpp	2016-05-29 19:01:36 UTC (rev 201495)
@@ -218,7 +218,7 @@
         length = jsCast<DirectArguments*>(cell)->length(callFrame);
         break;
     case ScopedArgumentsType:
-        length =jsCast<ScopedArguments*>(cell)->length(callFrame);
+        length = jsCast<ScopedArguments*>(cell)->length(callFrame);
         break;
     case StringType:
         callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame,  arguments));
@@ -226,8 +226,11 @@
     default:
         ASSERT(arguments.isObject());
         length = getLength(callFrame, jsCast<JSObject*>(cell));
+        if (UNLIKELY(callFrame->hadException()))
+            return 0;
         break;
     }
+
     
     if (length >= firstVarArgOffset)
         length -= firstVarArgOffset;

Modified: trunk/Source/_javascript_Core/runtime/InternalFunction.cpp (201494 => 201495)


--- trunk/Source/_javascript_Core/runtime/InternalFunction.cpp	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/runtime/InternalFunction.cpp	2016-05-29 19:01:36 UTC (rev 201495)
@@ -109,16 +109,16 @@
                 return structure;
 
             // Note, Reflect.construct might cause the profile to churn but we don't care.
-            JSObject* prototype = jsDynamicCast<JSObject*>(newTarget.get(exec, exec->propertyNames().prototype));
-            if (exec->hadException())
+            JSValue prototypeValue = newTarget.get(exec, exec->propertyNames().prototype);
+            if (UNLIKELY(vm.exception()))
                 return nullptr;
-            if (prototype)
+            if (JSObject* prototype = jsDynamicCast<JSObject*>(prototypeValue))
                 return targetFunction->rareData(vm)->createInternalFunctionAllocationStructureFromBase(vm, prototype, baseClass);
         } else {
-            JSObject* prototype = jsDynamicCast<JSObject*>(newTarget.get(exec, exec->propertyNames().prototype));
-            if (exec->hadException())
+            JSValue prototypeValue = newTarget.get(exec, exec->propertyNames().prototype);
+            if (UNLIKELY(vm.exception()))
                 return nullptr;
-            if (prototype) {
+            if (JSObject* prototype = jsDynamicCast<JSObject*>(prototypeValue)) {
                 // This only happens if someone Reflect.constructs our builtin constructor with another builtin constructor as the new.target.
                 // Thus, we don't care about the cost of looking up the structure from our hash table every time.
                 return vm.prototypeMap.emptyStructureForPrototypeFromBaseStructure(prototype, baseClass);

Modified: trunk/Source/_javascript_Core/runtime/JSArray.h (201494 => 201495)


--- trunk/Source/_javascript_Core/runtime/JSArray.h	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/runtime/JSArray.h	2016-05-29 19:01:36 UTC (rev 201495)
@@ -352,7 +352,12 @@
 {
     if (isJSArray(obj))
         return jsCast<JSArray*>(obj)->length();
-    return obj->get(exec, exec->propertyNames().length).toUInt32(exec);
+
+    VM& vm = exec->vm();
+    JSValue lengthValue = obj->get(exec, vm.propertyNames->length);
+    if (UNLIKELY(vm.exception()))
+        return UINT_MAX;
+    return lengthValue.toUInt32(exec);
 }
 
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/ObjectPrototype.cpp (201494 => 201495)


--- trunk/Source/_javascript_Core/runtime/ObjectPrototype.cpp	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/runtime/ObjectPrototype.cpp	2016-05-29 19:01:36 UTC (rev 201495)
@@ -282,6 +282,8 @@
         PropertySlot toStringTagSlot(thisObject, PropertySlot::InternalMethodType::Get);
         if (thisObject->getPropertySlot(exec, toStringTagSymbol, toStringTagSlot)) {
             JSValue stringTag = toStringTagSlot.getValue(exec, toStringTagSymbol);
+            if (UNLIKELY(vm.exception()))
+                return JSValue::encode(JSValue());
             if (stringTag.isString()) {
                 JSRopeString::RopeBuilder ropeBuilder(vm);
                 ropeBuilder.append(vm.smallStrings.objectStringStart());

Modified: trunk/Source/_javascript_Core/runtime/ProxyObject.cpp (201494 => 201495)


--- trunk/Source/_javascript_Core/runtime/ProxyObject.cpp	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/runtime/ProxyObject.cpp	2016-05-29 19:01:36 UTC (rev 201495)
@@ -34,6 +34,9 @@
 #include "SlotVisitorInlines.h"
 #include "StructureInlines.h"
 
+// Note that this file is compile with -fno-optimize-sibling-calls because we rely on the machine stack
+// growing larger for throwing OOM errors for when we have an effectively cyclic prototype chain.
+
 namespace JSC {
 
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ProxyObject);
@@ -95,7 +98,7 @@
 static EncodedJSValue performProxyGet(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName, JSObject* slotBase)
 {
     VM& vm = exec->vm();
-    if (!vm.isSafeToRecurse()) {
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
         throwStackOverflowError(exec);
         return JSValue::encode(JSValue());
     }
@@ -155,6 +158,10 @@
 bool ProxyObject::performInternalMethodGetOwnProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return false;
+    }
     JSObject* target = this->target();
 
     auto performDefaultGetOwnProperty = [&] {
@@ -257,6 +264,10 @@
 bool ProxyObject::performHasProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return false;
+    }
     JSObject* target = this->target();
     slot.setValue(this, None, jsUndefined()); // Nobody should rely on our value, but be safe and protect against any bad actors reading our value.
 
@@ -318,6 +329,10 @@
 
 bool ProxyObject::getOwnPropertySlotCommon(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
+    if (UNLIKELY(!exec->vm().isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return false;
+    }
     slot.disableCaching();
     slot.setIsTaintedByProxy();
     switch (slot.internalMethodType()) {
@@ -353,7 +368,7 @@
 bool ProxyObject::performPut(ExecState* exec, JSValue putValue, JSValue thisValue, PropertyName propertyName, PerformDefaultPutFunction performDefaultPut)
 {
     VM& vm = exec->vm();
-    if (!vm.isSafeToRecurse()) {
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
         throwStackOverflowError(exec);
         return false;
     }
@@ -445,6 +460,10 @@
 static EncodedJSValue JSC_HOST_CALL performProxyCall(ExecState* exec)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return JSValue::encode(JSValue());
+    }
     ProxyObject* proxy = jsCast<ProxyObject*>(exec->callee());
     JSValue handlerValue = proxy->handler();
     if (handlerValue.isNull())
@@ -490,6 +509,10 @@
 static EncodedJSValue JSC_HOST_CALL performProxyConstruct(ExecState* exec)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return JSValue::encode(JSValue());
+    }
     ProxyObject* proxy = jsCast<ProxyObject*>(exec->callee());
     JSValue handlerValue = proxy->handler();
     if (handlerValue.isNull())
@@ -541,6 +564,10 @@
 bool ProxyObject::performDelete(ExecState* exec, PropertyName propertyName, DefaultDeleteFunction performDefaultDelete)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return false;
+    }
 
     if (vm.propertyNames->isPrivateName(Identifier::fromUid(&vm, propertyName.uid())))
         return performDefaultDelete();
@@ -613,6 +640,10 @@
 bool ProxyObject::performPreventExtensions(ExecState* exec)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return false;
+    }
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
@@ -661,6 +692,10 @@
 bool ProxyObject::performIsExtensible(ExecState* exec)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return false;
+    }
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
@@ -715,6 +750,10 @@
 bool ProxyObject::performDefineOwnProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return false;
+    }
 
     JSObject* target = this->target();
     auto performDefaultDefineOwnProperty = [&] {
@@ -808,6 +847,10 @@
 void ProxyObject::performGetOwnPropertyNames(ExecState* exec, PropertyNameArray& trapResult, EnumerationMode enumerationMode)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return;
+    }
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
         throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
@@ -947,6 +990,11 @@
     thisObject->performGetOwnPropertyNames(exec, propertyNameArray, enumerationMode);
 }
 
+void ProxyObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNameArray, EnumerationMode enumerationMode)
+{
+    JSObject::getPropertyNames(object, exec, propertyNameArray, enumerationMode);
+}
+
 void ProxyObject::getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode)
 {
     RELEASE_ASSERT_NOT_REACHED();
@@ -968,6 +1016,10 @@
     ASSERT(prototype.isObject() || prototype.isNull());
 
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return false;
+    }
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
@@ -1028,6 +1080,10 @@
 JSValue ProxyObject::performGetPrototype(ExecState* exec)
 {
     VM& vm = exec->vm();
+    if (UNLIKELY(!vm.isSafeToRecurse())) {
+        throwStackOverflowError(exec);
+        return JSValue();
+    }
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {

Modified: trunk/Source/_javascript_Core/runtime/ProxyObject.h (201494 => 201495)


--- trunk/Source/_javascript_Core/runtime/ProxyObject.h	2016-05-29 04:47:41 UTC (rev 201494)
+++ trunk/Source/_javascript_Core/runtime/ProxyObject.h	2016-05-29 19:01:36 UTC (rev 201495)
@@ -36,8 +36,6 @@
 public:
     typedef JSNonFinalObject Base;
 
-    // We lie an say we override getPropertyNames() because it prevents
-    // property name enumeration caching.
     const static unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | TypeOfShouldCallGetCallData | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | ProhibitsPropertyCaching;
 
     static ProxyObject* create(ExecState* exec, JSGlobalObject* globalObject, JSValue target, JSValue handler)
@@ -87,6 +85,7 @@
     static bool isExtensible(JSObject*, ExecState*);
     static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
     static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+    static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     static NO_RETURN_DUE_TO_CRASH void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     static NO_RETURN_DUE_TO_CRASH void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     static NO_RETURN_DUE_TO_CRASH void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);

Added: trunk/Source/_javascript_Core/tests/stress/proxy-stack-overflow-exceptions.js (0 => 201495)


--- trunk/Source/_javascript_Core/tests/stress/proxy-stack-overflow-exceptions.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/proxy-stack-overflow-exceptions.js	2016-05-29 19:01:36 UTC (rev 201495)
@@ -0,0 +1,161 @@
+function shouldThrowStackOverflow(f) {
+    let threw = false;
+    const verbose = false;
+    try {
+        f();
+    } catch(e) {
+        threw = e instanceof RangeError;
+    }
+    if (!threw)
+        throw new Error("No stack overflow error thrown.");
+    if (verbose)
+        print("passed test: " + f + "\n\n");
+}
+
+const emptyFunction = function() { };
+let seenStartObjects = new Map;
+function makeLongProxyChain(startObject = emptyFunction) {
+    if (seenStartObjects.has(startObject))
+        return seenStartObjects.get(startObject);
+
+    let p = new Proxy(startObject, { });
+    for (let i = 0; i < 500000; i++)
+        p = new Proxy(p, {});
+    seenStartObjects.set(startObject, p);
+    return p;
+}
+
+shouldThrowStackOverflow(function longProxyChain() {
+    let f = makeLongProxyChain();
+    f.name;
+});
+
+shouldThrowStackOverflow(function effecivelyCyclicProxyProtoChain1() {
+    let t = {};
+    let p = new Proxy(t, {}); 
+    Object.setPrototypeOf(t, p); 
+    t.propertyDoesNotExist;
+});
+
+shouldThrowStackOverflow(function effecivelyCyclicProxyProtoChain2() {
+    let t = {};
+    let p = new Proxy(t, {}); 
+    Object.setPrototypeOf(t, p); 
+    for (var k in p)
+        break;
+});
+
+shouldThrowStackOverflow(function effecivelyCyclicProxyProtoChain3() {
+    let t = {}; 
+    let p = new Proxy(t, {});
+    Object.setPrototypeOf(t, p);
+    Object.prototype.toString.call(p);
+});
+
+shouldThrowStackOverflow(function longProxyChainBind() {
+    let p = makeLongProxyChain();
+    Function.prototype.bind.call(p)
+});
+
+shouldThrowStackOverflow(function longProxyChainPropertyAccess() {
+    let p = makeLongProxyChain();
+    p.nonExistentProperty;
+});
+
+shouldThrowStackOverflow(function longProxyChainReflectConstruct() {
+    let p = makeLongProxyChain();
+    Reflect.construct(Array, [], p);
+});
+
+shouldThrowStackOverflow(function longProxyChainReflectSet() {
+    let p = makeLongProxyChain();
+    Reflect.set([null], 0, 0, p);
+});
+
+shouldThrowStackOverflow(function longProxyChainReflectOwnKeys() {
+    let p = makeLongProxyChain();
+    Reflect.ownKeys(p);
+});
+
+shouldThrowStackOverflow(function longProxyChainGetPrototypeOf() {
+    let p = makeLongProxyChain();
+    Reflect.getPrototypeOf(p);
+});
+
+shouldThrowStackOverflow(function longProxyChainSetPrototypeOf() {
+    let p = makeLongProxyChain();
+    Reflect.setPrototypeOf(p, null);
+});
+
+shouldThrowStackOverflow(function longProxyChainGetOwnPropertyDescriptor() {
+    let p = makeLongProxyChain();
+    Reflect.getOwnPropertyDescriptor(p, "");
+});
+
+shouldThrowStackOverflow(function longProxyChainDefineProperty() {
+    let p = makeLongProxyChain();
+    Reflect.defineProperty(p, "", {});
+});
+
+shouldThrowStackOverflow(function longProxyChainIsExtensible() {
+    let p = makeLongProxyChain();
+    Reflect.isExtensible(p);
+});
+
+shouldThrowStackOverflow(function longProxyChainPreventExtensions() {
+    let p = makeLongProxyChain();
+    Reflect.preventExtensions(p)
+});
+
+shouldThrowStackOverflow(function longProxyChainDeleteProperty() {
+    let p = makeLongProxyChain();
+    Reflect.deleteProperty(p, "");
+});
+
+shouldThrowStackOverflow(function longProxyChainWithScope() {
+    let p = makeLongProxyChain();
+    with (p) {
+        propertyLookup;
+    }
+});
+
+shouldThrowStackOverflow(function longProxyChainWithScope2() {
+    let p = makeLongProxyChain();
+    with (p) {
+        storeToProperty = 0;
+    }
+});
+
+shouldThrowStackOverflow(function longProxyChainWithScope3() {
+    let p = makeLongProxyChain();
+    with (p) {
+        someFunctionPropertyLookup()
+    }
+});
+
+shouldThrowStackOverflow(function longProxyChainArrayPrototypePush() {
+    let p = makeLongProxyChain();
+    Array.prototype.push.call(p, 0);
+});
+
+shouldThrowStackOverflow(function longProxyChainWithScope4() {
+    let p = makeLongProxyChain();
+    with (p) {
+        eval("");
+    }
+});
+
+shouldThrowStackOverflow(function longProxyChainCall() {
+    let p = makeLongProxyChain();
+    p();
+});
+
+shouldThrowStackOverflow(function longProxyChainConstruct() {
+    let p = makeLongProxyChain();
+    new p;
+});
+
+shouldThrowStackOverflow(function longProxyChainHas() {
+    let p = makeLongProxyChain();
+    Reflect.has(p, "foo");
+});
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to