2011-02-01 chris reiss <christopher.reiss@nokia.com>
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Feb 2011 00:22:52 +0000 (00:22 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Feb 2011 00:22:52 +0000 (00:22 +0000)
        Reviewed by Adam Barth.

        Self-replicating code makes Safari hang and eventually crash
        https://bugs.webkit.org/show_bug.cgi?id=15123

        * fast/dom/Document/document-close-iframe-load-expected.txt: Added.
        * fast/dom/Document/document-close-iframe-load.html: Added.
        * fast/dom/Document/document-close-nested-iframe-load-expected.txt: Added.
        * fast/dom/Document/document-close-nested-iframe-load.html: Added.
        * fast/dom/Document/document-write-recursion-expected.txt: Added.
        * fast/dom/Document/document-write-recursion.html: Added.
2011-02-01  chris reiss  <christopher.reiss@nokia.com>

        Reviewed by Adam Barth.

        Self-replicating code makes Safari hang and eventually crash
        https://bugs.webkit.org/show_bug.cgi?id=15123

        Here we are replicating the Firefox safeguard against
        recursive document.write( ) 's.

        See  https://bug197052.bugzilla.mozilla.org/attachment.cgi?id=293907 in bug
        https://bugzilla.mozilla.org/show_bug.cgi?id=197052 .   Firefox does two things -
            a) imposes a recursion limit of 20 on document.write( ) and
            b) once that limit is passed, panics all the way the call stack (rather than just returning one level.)
        To see why this is necessary, consider the script :

        <script>
           var t = document.body.innerHTML;
           document.write(t);
        </script>

        This will create a tree both broad and deep as the script keeps appending itself to the text.   If
        we just return one level after the recursion limit is reached, we still allow millions of copies to
        duplicate (and execute).

        The recursion is fortunately depth-first, so as soon as we cross this limit, we panic up the callstack
        to prevent this situation.    (IE apparently does the same thing, with a lower recursion limit.)

        Test: fast/dom/Document/document-write-recursion.html
        Test: fast/dom/Document/document-close-iframe-load.html
        Test: fast/dom/Document/document-close-nested-iframe-load.html

        * dom/Document.cpp:
        (WebCore::Document::Document):
        (WebCore::Document::write):
        * dom/Document.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@77333 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/dom/Document/document-close-iframe-load-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Document/document-close-iframe-load.html [new file with mode: 0644]
LayoutTests/fast/dom/Document/document-close-nested-iframe-load-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Document/document-close-nested-iframe-load.html [new file with mode: 0644]
LayoutTests/fast/dom/Document/document-write-recursion-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Document/document-write-recursion.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h

index 95f9f42..fbca721 100644 (file)
@@ -1,3 +1,17 @@
+2011-02-01  chris reiss  <christopher.reiss@nokia.com>
+
+        Reviewed by Adam Barth.
+
+        Self-replicating code makes Safari hang and eventually crash
+        https://bugs.webkit.org/show_bug.cgi?id=15123
+
+        * fast/dom/Document/document-close-iframe-load-expected.txt: Added.
+        * fast/dom/Document/document-close-iframe-load.html: Added.
+        * fast/dom/Document/document-close-nested-iframe-load-expected.txt: Added.
+        * fast/dom/Document/document-close-nested-iframe-load.html: Added.
+        * fast/dom/Document/document-write-recursion-expected.txt: Added.
+        * fast/dom/Document/document-write-recursion.html: Added.
+
 2011-02-01  Dimitri Glazkov  <dglazkov@chromium.org>
 
         Updated text expectations to avoid conflict.
diff --git a/LayoutTests/fast/dom/Document/document-close-iframe-load-expected.txt b/LayoutTests/fast/dom/Document/document-close-iframe-load-expected.txt
new file mode 100644 (file)
index 0000000..2c7db1a
--- /dev/null
@@ -0,0 +1,2 @@
+ALERT: PASS (just to make expected.txt non-empty)
+
diff --git a/LayoutTests/fast/dom/Document/document-close-iframe-load.html b/LayoutTests/fast/dom/Document/document-close-iframe-load.html
new file mode 100644 (file)
index 0000000..3f5f949
--- /dev/null
@@ -0,0 +1,7 @@
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+alert('PASS (just to make expected.txt non-empty)');
+</script>
+This test passes if it doesn't crash.
+<iframe onload="document.open();document.close();" >
diff --git a/LayoutTests/fast/dom/Document/document-close-nested-iframe-load-expected.txt b/LayoutTests/fast/dom/Document/document-close-nested-iframe-load-expected.txt
new file mode 100644 (file)
index 0000000..4df18d9
--- /dev/null
@@ -0,0 +1 @@
+/PASS/
diff --git a/LayoutTests/fast/dom/Document/document-close-nested-iframe-load.html b/LayoutTests/fast/dom/Document/document-close-nested-iframe-load.html
new file mode 100644 (file)
index 0000000..1ada9eb
--- /dev/null
@@ -0,0 +1,6 @@
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+</script>
+FAIL
+<iframe onload="document.open(); document.close(); document.write('<iframe onload=document.open();document.close();document.write(/PASS/); >')"></iframe>
diff --git a/LayoutTests/fast/dom/Document/document-write-recursion-expected.txt b/LayoutTests/fast/dom/Document/document-write-recursion-expected.txt
new file mode 100644 (file)
index 0000000..7ef22e9
--- /dev/null
@@ -0,0 +1 @@
+PASS
diff --git a/LayoutTests/fast/dom/Document/document-write-recursion.html b/LayoutTests/fast/dom/Document/document-write-recursion.html
new file mode 100644 (file)
index 0000000..19c6e63
--- /dev/null
@@ -0,0 +1,11 @@
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+</script>
+<body>
+<script>
+    var t = document.body.innerHTML;
+    document.write(t);
+</script>
+PASS
+</body>
index 7b7ac7d..43db316 100644 (file)
@@ -1,3 +1,42 @@
+2011-02-01  chris reiss  <christopher.reiss@nokia.com>
+
+        Reviewed by Adam Barth.
+
+        Self-replicating code makes Safari hang and eventually crash
+        https://bugs.webkit.org/show_bug.cgi?id=15123
+
+       
+        Here we are replicating the Firefox safeguard against
+        recursive document.write( ) 's.
+
+        See  https://bug197052.bugzilla.mozilla.org/attachment.cgi?id=293907 in bug 
+        https://bugzilla.mozilla.org/show_bug.cgi?id=197052 .   Firefox does two things - 
+            a) imposes a recursion limit of 20 on document.write( ) and
+            b) once that limit is passed, panics all the way the call stack (rather than just returning one level.)
+        To see why this is necessary, consider the script : 
+
+        <script>
+           var t = document.body.innerHTML;
+           document.write(t);
+        </script> 
+
+        This will create a tree both broad and deep as the script keeps appending itself to the text.   If
+        we just return one level after the recursion limit is reached, we still allow millions of copies to 
+        duplicate (and execute).   
+
+        The recursion is fortunately depth-first, so as soon as we cross this limit, we panic up the callstack
+        to prevent this situation.    (IE apparently does the same thing, with a lower recursion limit.) 
+
+        Test: fast/dom/Document/document-write-recursion.html        
+        Test: fast/dom/Document/document-close-iframe-load.html
+        Test: fast/dom/Document/document-close-nested-iframe-load.html
+
+
+        * dom/Document.cpp:
+        (WebCore::Document::Document):
+        (WebCore::Document::write):
+        * dom/Document.h:
+
 2011-02-01  Johnny Ding  <jnd@chromium.org>
 
         Reviewed by Darin Adler.
index 0f4f4c5..72a4f81 100644 (file)
 #include "MouseEventWithHitTestResults.h"
 #include "MutationEvent.h"
 #include "NameNodeList.h"
+#include "NestingLevelIncrementer.h"
 #include "NodeFilter.h"
 #include "NodeIterator.h"
 #include "NodeWithIndex.h"
@@ -221,6 +222,8 @@ using namespace HTMLNames;
 
 // #define INSTRUMENT_LAYOUT_SCHEDULING 1
 
+static const unsigned cMaxWriteRecursionDepth = 21;
+
 // This amount of time must have elapsed before we will even consider scheduling a layout without a delay.
 // FIXME: For faster machines this value can really be lowered to 200.  250 is adequate, but a little high
 // for dual G5s. :)
@@ -424,6 +427,8 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML, con
     , m_loadEventDelayTimer(this, &Document::loadEventDelayTimerFired)
     , m_directionSetOnDocumentElement(false)
     , m_writingModeSetOnDocumentElement(false)
+    , m_writeRecursionIsTooDeep(false)
+    , m_writeRecursionDepth(0)
 #if ENABLE(REQUEST_ANIMATION_FRAME)
     , m_nextRequestAnimationFrameCallbackId(0)
 #endif
@@ -2166,6 +2171,14 @@ int Document::elapsedTime() const
 
 void Document::write(const SegmentedString& text, Document* ownerDocument)
 {
+    NestingLevelIncrementer nestingLevelIncrementer(m_writeRecursionDepth);
+
+    m_writeRecursionIsTooDeep = (m_writeRecursionDepth > 1) && m_writeRecursionIsTooDeep;
+    m_writeRecursionIsTooDeep = (m_writeRecursionDepth > cMaxWriteRecursionDepth) || m_writeRecursionIsTooDeep;
+
+    if (m_writeRecursionIsTooDeep)
+       return;
+
 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
     if (!ownerElement())
         printf("Beginning a document.write at %d\n", elapsedTime());
index 170822b..5a745e4 100644 (file)
@@ -1382,6 +1382,8 @@ private:
 
     DocumentTiming m_documentTiming;
     RefPtr<MediaQueryMatcher> m_mediaQueryMatcher;
+    bool m_writeRecursionIsTooDeep;
+    unsigned m_writeRecursionDepth;
 
 #if ENABLE(REQUEST_ANIMATION_FRAME)
     typedef Vector<RefPtr<RequestAnimationFrameCallback> > RequestAnimationFrameCallbackList;