Commit 4cd37c31 authored by darin@apple.com's avatar darin@apple.com

2011-01-18 Darin Adler <darin@apple.com>

        Reviewed by Geoffrey Garen.

        Stack overflow when converting an Error object to string
        https://bugs.webkit.org/show_bug.cgi?id=46410

        * fast/js/script-tests/toString-recursion.js: Added.
        * fast/js/toString-recursion-expected.txt: Added.
        * fast/js/toString-recursion.html: Added.
2011-01-18  Darin Adler  <darin@apple.com>

        Reviewed by Geoffrey Garen.

        Stack overflow when converting an Error object to string
        https://bugs.webkit.org/show_bug.cgi?id=46410

        * Android.mk: Added StringRecursionChecker.cpp and
        StringRecursionChecker.h.
        * CMakeLists.txt: Ditto.
        * GNUmakefile.am: Ditto.
        * JavaScriptCore.gypi: Ditto.
        * JavaScriptCore.pro: Ditto.
        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Ditto.
        * JavaScriptCore.xcodeproj/project.pbxproj: Ditto.

        * runtime/ArrayPrototype.cpp:
        (JSC::arrayProtoFuncToString): Use StringRecursionChecker instead
        of the older hand-written code to do the same thing.
        (JSC::arrayProtoFuncToLocaleString): Ditto.
        (JSC::arrayProtoFuncJoin): Ditto.

        * runtime/ErrorPrototype.cpp:
        (JSC::errorProtoFuncToString): Use StringRecursionChecker.

        * runtime/JSGlobalData.h: Renamed arrayVisitedElements to
        stringRecursionCheckVisitedObjects.

        * runtime/RegExpPrototype.cpp:
        (JSC::regExpProtoFuncToString): Use StringRecursionChecker.

        * runtime/StringRecursionChecker.cpp: Added.
        * runtime/StringRecursionChecker.h: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@76185 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent a9399231
2011-01-18 Darin Adler <darin@apple.com>
Reviewed by Geoffrey Garen.
Stack overflow when converting an Error object to string
https://bugs.webkit.org/show_bug.cgi?id=46410
* fast/js/script-tests/toString-recursion.js: Added.
* fast/js/toString-recursion-expected.txt: Added.
* fast/js/toString-recursion.html: Added.
2011-01-19 Oliver Hunt <oliver@apple.com>
Reviewed by Gavin Barraclough.
......
description(
"This test checks that toString() does not have a problem when an object has itself as a property."
);
// Array (elements)
shouldBe("var array = []; array[0] = array; array + ''", "''");
// Error (name, message)
shouldBe("var error = new Error; error.name = error; error.message = error; error + ''", "': '");
// RegExp (source)
shouldBe("var regexp = /a/; regexp.source = regexp; regexp + ''", "'/a/'");
var successfullyParsed = true;
This test checks that toString() does not have a problem when an object has itself as a property.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS var array = []; array[0] = array; array + '' is ''
PASS var error = new Error; error.name = error; error.message = error; error + '' is ': '
PASS var regexp = /a/; regexp.source = regexp; regexp + '' is '/a/'
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" href="resources/js-test-style.css">
<script src="resources/js-test-pre.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script src="script-tests/toString-recursion.js"></script>
<script src="resources/js-test-post.js"></script>
</body>
</html>
......@@ -151,6 +151,7 @@ LOCAL_SRC_FILES := \
runtime/StringConstructor.cpp \
runtime/StringObject.cpp \
runtime/StringPrototype.cpp \
runtime/StringRecursionChecker.cpp \
runtime/Structure.cpp \
runtime/StructureChain.cpp \
runtime/TimeoutChecker.cpp \
......
......@@ -163,6 +163,7 @@ SET(JavaScriptCore_SOURCES
runtime/StringConstructor.cpp
runtime/StringObject.cpp
runtime/StringPrototype.cpp
runtime/StringRecursionChecker.cpp
runtime/Structure.cpp
runtime/StructureChain.cpp
runtime/TimeoutChecker.cpp
......
2011-01-18 Darin Adler <darin@apple.com>
Reviewed by Geoffrey Garen.
Stack overflow when converting an Error object to string
https://bugs.webkit.org/show_bug.cgi?id=46410
* Android.mk: Added StringRecursionChecker.cpp and
StringRecursionChecker.h.
* CMakeLists.txt: Ditto.
* GNUmakefile.am: Ditto.
* JavaScriptCore.gypi: Ditto.
* JavaScriptCore.pro: Ditto.
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Ditto.
* JavaScriptCore.xcodeproj/project.pbxproj: Ditto.
* runtime/ArrayPrototype.cpp:
(JSC::arrayProtoFuncToString): Use StringRecursionChecker instead
of the older hand-written code to do the same thing.
(JSC::arrayProtoFuncToLocaleString): Ditto.
(JSC::arrayProtoFuncJoin): Ditto.
* runtime/ErrorPrototype.cpp:
(JSC::errorProtoFuncToString): Use StringRecursionChecker.
* runtime/JSGlobalData.h: Renamed arrayVisitedElements to
stringRecursionCheckVisitedObjects.
* runtime/RegExpPrototype.cpp:
(JSC::regExpProtoFuncToString): Use StringRecursionChecker.
* runtime/StringRecursionChecker.cpp: Added.
* runtime/StringRecursionChecker.h: Added.
2011-01-19 Oliver Hunt <oliver@apple.com>
Reviewed by Gavin Barraclough.
......
......@@ -404,6 +404,8 @@ javascriptcore_sources += \
Source/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h \
Source/JavaScriptCore/runtime/StringPrototype.cpp \
Source/JavaScriptCore/runtime/StringPrototype.h \
Source/JavaScriptCore/runtime/StringRecursionChecker.cpp \
Source/JavaScriptCore/runtime/StringRecursionChecker.h \
Source/JavaScriptCore/runtime/StructureChain.cpp \
Source/JavaScriptCore/runtime/StructureChain.h \
Source/JavaScriptCore/runtime/Structure.cpp \
......
......@@ -337,6 +337,8 @@
'runtime/StringObjectThatMasqueradesAsUndefined.h',
'runtime/StringPrototype.cpp',
'runtime/StringPrototype.h',
'runtime/StringRecursionChecker.cpp',
'runtime/StringRecursionChecker.h',
'runtime/Structure.cpp',
'runtime/Structure.h',
'runtime/StructureChain.cpp',
......
......@@ -205,6 +205,7 @@ SOURCES += \
runtime/StringConstructor.cpp \
runtime/StringObject.cpp \
runtime/StringPrototype.cpp \
runtime/StringRecursionChecker.cpp \
runtime/StructureChain.cpp \
runtime/Structure.cpp \
runtime/TimeoutChecker.cpp \
......
......@@ -1245,6 +1245,14 @@
RelativePath="..\..\runtime\StringPrototype.h"
>
</File>
<File
RelativePath="..\..\runtime\StringRecursionChecker.cpp"
>
</File>
<File
RelativePath="..\..\runtime\StringRecursionChecker.h"
>
</File>
<File
RelativePath="..\..\runtime\Structure.cpp"
>
......
......@@ -294,6 +294,7 @@
932F5BEA0822A1C700736975 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 932F5BD90822A1C700736975 /* JavaScriptCore.framework */; };
933040040E6A749400786E6A /* SmallStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = 93303FEA0E6A72C000786E6A /* SmallStrings.h */; settings = {ATTRIBUTES = (Private, ); }; };
9330402C0E6A764000786E6A /* SmallStrings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93303FE80E6A72B500786E6A /* SmallStrings.cpp */; };
9335F24D12E6765B002B5553 /* StringRecursionChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93345A8712D838C400302BE3 /* StringRecursionChecker.cpp */; };
933F5CDC1269229B0049191E /* NullPtr.h in Headers */ = {isa = PBXBuildFile; fileRef = 933F5CDB126922690049191E /* NullPtr.h */; settings = {ATTRIBUTES = (Private, ); }; };
937013480CA97E0E00FA14D3 /* pcre_ucp_searchfuncs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 937013470CA97E0E00FA14D3 /* pcre_ucp_searchfuncs.cpp */; settings = {COMPILER_FLAGS = "-Wno-sign-compare"; }; };
93854A9A12C93D3B00DAAF77 /* NullPtr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93854A9912C93D3B00DAAF77 /* NullPtr.cpp */; };
......@@ -925,6 +926,8 @@
932F5BE10822A1C700736975 /* jsc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jsc; sourceTree = BUILT_PRODUCTS_DIR; };
93303FE80E6A72B500786E6A /* SmallStrings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SmallStrings.cpp; sourceTree = "<group>"; };
93303FEA0E6A72C000786E6A /* SmallStrings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmallStrings.h; sourceTree = "<group>"; };
93345A8712D838C400302BE3 /* StringRecursionChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringRecursionChecker.cpp; sourceTree = "<group>"; };
93345A8812D838C400302BE3 /* StringRecursionChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringRecursionChecker.h; sourceTree = "<group>"; };
933A349A038AE7C6008635CE /* Identifier.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = Identifier.h; sourceTree = "<group>"; tabWidth = 8; };
933A349D038AE80F008635CE /* Identifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Identifier.cpp; sourceTree = "<group>"; tabWidth = 8; };
933F5CDB126922690049191E /* NullPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NullPtr.h; sourceTree = "<group>"; };
......@@ -1867,6 +1870,8 @@
BC18C3C40E16EE3300B34460 /* StringObjectThatMasqueradesAsUndefined.h */,
BC18C3C50E16EE3300B34460 /* StringPrototype.cpp */,
BC18C3C60E16EE3300B34460 /* StringPrototype.h */,
93345A8712D838C400302BE3 /* StringRecursionChecker.cpp */,
93345A8812D838C400302BE3 /* StringRecursionChecker.h */,
BCDE3AB00E6C82CF001453A7 /* Structure.cpp */,
BCDE3AB10E6C82CF001453A7 /* Structure.h */,
7E4EE70E0EBB7A5B005934AA /* StructureChain.cpp */,
......@@ -2873,6 +2878,7 @@
86704B8612DBA33700A9FE7B /* YarrJIT.cpp in Sources */,
86704B8912DBA33700A9FE7B /* YarrPattern.cpp in Sources */,
86704B4212DB8A8100A9FE7B /* YarrSyntaxChecker.cpp in Sources */,
9335F24D12E6765B002B5553 /* StringRecursionChecker.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2003, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
* Copyright (C) 2003 Peter Kelly (pmk@post.com)
* Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
*
......@@ -32,6 +32,7 @@
#include "Lookup.h"
#include "ObjectPrototype.h"
#include "Operations.h"
#include "StringRecursionChecker.h"
#include <algorithm>
#include <wtf/Assertions.h>
#include <wtf/HashSet.h>
......@@ -168,15 +169,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
return throwVMTypeError(exec);
JSArray* thisObj = asArray(thisValue);
HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
return throwVMError(exec, createStackOverflowError(exec));
}
bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
if (alreadyVisited)
return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoiding infinite recursion.
StringRecursionChecker checker(exec, thisObj);
if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
return earlyReturnValue;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned totalSize = length ? length - 1 : 0;
......@@ -209,7 +204,6 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
if (exec->hadException())
break;
}
arrayVisitedElements.remove(thisObj);
if (!totalSize)
return JSValue::encode(jsEmptyString(exec));
Vector<UChar> buffer;
......@@ -234,15 +228,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
return throwVMTypeError(exec);
JSObject* thisObj = asArray(thisValue);
HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
return throwVMError(exec, createStackOverflowError(exec));
}
bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
if (alreadyVisited)
return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion.
StringRecursionChecker checker(exec, thisObj);
if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
return earlyReturnValue;
JSStringBuilder strBuffer;
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
......@@ -264,7 +252,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
strBuffer.append(str);
}
}
arrayVisitedElements.remove(thisObj);
return JSValue::encode(strBuffer.build(exec));
}
......@@ -272,15 +260,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
return throwVMError(exec, createStackOverflowError(exec));
}
bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
if (alreadyVisited)
return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion.
StringRecursionChecker checker(exec, thisObj);
if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
return earlyReturnValue;
JSStringBuilder strBuffer;
......@@ -335,7 +317,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
if (!element.isUndefinedOrNull())
strBuffer.append(element.toString(exec));
}
arrayVisitedElements.remove(thisObj);
return JSValue::encode(strBuffer.build(exec));
}
......
......@@ -26,6 +26,7 @@
#include "JSStringBuilder.h"
#include "ObjectPrototype.h"
#include "PrototypeFunction.h"
#include "StringRecursionChecker.h"
#include "UString.h"
namespace JSC {
......@@ -47,6 +48,11 @@ ErrorPrototype::ErrorPrototype(ExecState* exec, JSGlobalObject* globalObject, No
EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec)
{
JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
StringRecursionChecker checker(exec, thisObj);
if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
return earlyReturnValue;
JSValue name = thisObj->get(exec, exec->propertyNames().name);
JSValue message = thisObj->get(exec, exec->propertyNames().message);
......
......@@ -214,7 +214,7 @@ namespace JSC {
JSGlobalObject* head;
JSGlobalObject* dynamicGlobalObject;
HashSet<JSObject*> arrayVisitedElements;
HashSet<JSObject*> stringRecursionCheckVisitedObjects;
Stringifier* firstStringifierToMark;
......
......@@ -34,6 +34,7 @@
#include "RegExpObject.h"
#include "RegExp.h"
#include "RegExpCache.h"
#include "StringRecursionChecker.h"
#include "UStringConcatenate.h"
namespace JSC {
......@@ -111,15 +112,21 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec)
return throwVMTypeError(exec);
}
RegExpObject* thisObject = asRegExpObject(thisValue);
StringRecursionChecker checker(exec, thisObject);
if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
return earlyReturnValue;
char postfix[5] = { '/', 0, 0, 0, 0 };
int index = 1;
if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().global).toBoolean(exec))
if (thisObject->get(exec, exec->propertyNames().global).toBoolean(exec))
postfix[index++] = 'g';
if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().ignoreCase).toBoolean(exec))
if (thisObject->get(exec, exec->propertyNames().ignoreCase).toBoolean(exec))
postfix[index++] = 'i';
if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().multiline).toBoolean(exec))
if (thisObject->get(exec, exec->propertyNames().multiline).toBoolean(exec))
postfix[index] = 'm';
UString source = asRegExpObject(thisValue)->get(exec, exec->propertyNames().source).toString(exec);
UString source = thisObject->get(exec, exec->propertyNames().source).toString(exec);
// If source is empty, use "/(?:)/" to avoid colliding with comment syntax
return JSValue::encode(jsMakeNontrivialString(exec, "/", source.length() ? source : UString("(?:)"), postfix));
}
......
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "config.h"
#include "StringRecursionChecker.h"
#include "Error.h"
#include "ExceptionHelpers.h"
namespace JSC {
EncodedJSValue StringRecursionChecker::throwStackOverflowError()
{
return throwVMError(m_exec, createStackOverflowError(m_exec));
}
EncodedJSValue StringRecursionChecker::emptyString()
{
return JSValue::encode(jsEmptyString(m_exec));
}
}
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef StringRecursionChecker_h
#define StringRecursionChecker_h
#include "Interpreter.h"
namespace JSC {
class StringRecursionChecker {
WTF_MAKE_NONCOPYABLE(StringRecursionChecker);
public:
StringRecursionChecker(ExecState*, JSObject* thisObject);
~StringRecursionChecker();
EncodedJSValue earlyReturnValue() const; // 0 if everything is OK, value to return for failure cases
private:
EncodedJSValue throwStackOverflowError();
EncodedJSValue emptyString();
EncodedJSValue performCheck();
ExecState* m_exec;
JSObject* m_thisObject;
EncodedJSValue m_earlyReturnValue;
};
inline EncodedJSValue StringRecursionChecker::performCheck()
{
int size = m_exec->globalData().stringRecursionCheckVisitedObjects.size();
if (size >= MaxSmallThreadReentryDepth && size >= m_exec->globalData().maxReentryDepth)
return throwStackOverflowError();
bool alreadyVisited = !m_exec->globalData().stringRecursionCheckVisitedObjects.add(m_thisObject).second;
if (alreadyVisited)
return emptyString(); // Return empty string to avoid infinite recursion.
return 0; // Indicate success.
}
inline StringRecursionChecker::StringRecursionChecker(ExecState* exec, JSObject* thisObject)
: m_exec(exec)
, m_thisObject(thisObject)
, m_earlyReturnValue(performCheck())
{
}
inline EncodedJSValue StringRecursionChecker::earlyReturnValue() const
{
return m_earlyReturnValue;
}
inline StringRecursionChecker::~StringRecursionChecker()
{
if (m_earlyReturnValue)
return;
ASSERT(m_exec->globalData().stringRecursionCheckVisitedObjects.contains(m_thisObject));
m_exec->globalData().stringRecursionCheckVisitedObjects.remove(m_thisObject);
}
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment