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:
* JavaScriptCore.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):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@201495 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index f361c52..67c5027 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -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(JavaScriptCore_OBJECT_LUT_SOURCES
runtime/ArrayConstructor.cpp
runtime/ArrayIteratorPrototype.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 26925e92..5e6901b 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,88 @@
+2016-05-29 Saam barati <sbarati@apple.com>
+
+ 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:
+ * JavaScriptCore.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 <akling@apple.com>
JSGlobalLexicalEnvironment leaks SegmentedVector due to lack of destructor.
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index fdef50a..3d55d9b 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -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, ); }; };
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index 84b1210..2ed8898 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -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;
diff --git a/Source/JavaScriptCore/runtime/InternalFunction.cpp b/Source/JavaScriptCore/runtime/InternalFunction.cpp
index 537b246..3cedbb7 100644
--- a/Source/JavaScriptCore/runtime/InternalFunction.cpp
+++ b/Source/JavaScriptCore/runtime/InternalFunction.cpp
@@ -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);
diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h
index bcc6e67..1b2c46c 100644
--- a/Source/JavaScriptCore/runtime/JSArray.h
+++ b/Source/JavaScriptCore/runtime/JSArray.h
@@ -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
diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp
index 55cf5e2..687058b 100644
--- a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp
@@ -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());
diff --git a/Source/JavaScriptCore/runtime/ProxyObject.cpp b/Source/JavaScriptCore/runtime/ProxyObject.cpp
index 998333c..88e634d 100644
--- a/Source/JavaScriptCore/runtime/ProxyObject.cpp
+++ b/Source/JavaScriptCore/runtime/ProxyObject.cpp
@@ -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()) {
diff --git a/Source/JavaScriptCore/runtime/ProxyObject.h b/Source/JavaScriptCore/runtime/ProxyObject.h
index 6d11abf..11de6f6 100644
--- a/Source/JavaScriptCore/runtime/ProxyObject.h
+++ b/Source/JavaScriptCore/runtime/ProxyObject.h
@@ -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);
diff --git a/Source/JavaScriptCore/tests/stress/proxy-stack-overflow-exceptions.js b/Source/JavaScriptCore/tests/stress/proxy-stack-overflow-exceptions.js
new file mode 100644
index 0000000..c28ab94
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/proxy-stack-overflow-exceptions.js
@@ -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");
+});