WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
15123
Self-replicating code makes Safari hang and eventually crash
https://bugs.webkit.org/show_bug.cgi?id=15123
Summary
Self-replicating code makes Safari hang and eventually crash
mitz
Reported
2007-08-31 05:44:20 PDT
The attached test case makes Safari hang and eventually crash.
Attachments
Test case (will crash)
(83 bytes, text/html)
2007-08-31 05:45 PDT
,
mitz
no flags
Details
defense against infinite recursion in document.write( )
(3.64 KB, patch)
2011-01-14 14:50 PST
,
chris reiss
abarth
: review-
Details
Formatted Diff
Diff
updated fix to infinite recursion in document.write( )
(3.95 KB, patch)
2011-01-17 13:55 PST
,
chris reiss
no flags
Details
Formatted Diff
Diff
updated fix w/ tabs(!) removed
(3.95 KB, patch)
2011-01-17 14:07 PST
,
chris reiss
kling
: review-
Details
Formatted Diff
Diff
multiple style fixes, layout tests removed, changelogs updated
(4.51 KB, patch)
2011-01-19 11:24 PST
,
chris reiss
no flags
Details
Formatted Diff
Diff
fix for document write recursion
(4.51 KB, patch)
2011-01-24 14:11 PST
,
chris reiss
no flags
Details
Formatted Diff
Diff
same as above, updated to merge cleanly
(4.67 KB, patch)
2011-01-25 10:23 PST
,
chris reiss
no flags
Details
Formatted Diff
Diff
write recursion fix
(8.04 KB, patch)
2011-01-28 13:09 PST
,
chris reiss
no flags
Details
Formatted Diff
Diff
write recursion fix
(8.08 KB, text/plain)
2011-01-31 09:27 PST
,
chris reiss
no flags
Details
write recursion fix
(8.08 KB, patch)
2011-01-31 09:32 PST
,
chris reiss
abarth
: review+
commit-queue
: commit-queue-
Details
Formatted Diff
Diff
write recursion fix
(7.78 KB, patch)
2011-02-01 14:22 PST
,
chris reiss
no flags
Details
Formatted Diff
Diff
Show Obsolete
(9)
View All
Add attachment
proposed patch, testcase, etc.
mitz
Comment 1
2007-08-31 05:45:17 PDT
Created
attachment 16168
[details]
Test case (will crash) Causes Firefox to hang, too.
David Kilzer (:ddkilzer)
Comment 2
2007-08-31 07:29:24 PDT
<
rdar://problem/5453375
>
David Kilzer (:ddkilzer)
Comment 3
2007-08-31 07:34:36 PDT
Not a regression as Safari 2.0.4 (419.3) with original WebKit on Mac OS X 10.4.10 (8R218) also exhibits this behavior. Console message (repeated ad nauseum until crash): [6861]
http://bugs.webkit.org/attachment.cgi?id=16168&action=view:Error
- Recursion too deep
chris reiss
Comment 4
2011-01-05 08:38:52 PST
I've started working on this ...
chris reiss
Comment 5
2011-01-10 13:10:02 PST
This creates infinite recursion in WebCore::Document::write( ). A simple fix seems to be to simply limit the depth of this recursion. Would this break anything? It's hard for me to envision a real world example more than a level or two deep (and those are pretty strange.) Feedback welcome ...
chris reiss
Comment 6
2011-01-10 14:26:17 PST
I tested this on Firefox (3.6.3, ubuntu release) and it handled this gracefully, bailing at 150 levels deep. I see mention of this bug here
https://bugzilla.mozilla.org/show_bug.cgi?id=82042
. Their solution refers vaguely to 'recursion checks', i'm looking for the exact gecko code that implements this.
chris reiss
Comment 7
2011-01-11 07:41:57 PST
So gecko fixed this bug here,
https://bugzilla.mozilla.org/show_bug.cgi?id=197052
. The patch imposes a recursion limit on document.write( ) of 20 (not 150, sorry.) Since this has survived since mid 2008, this appears to be a safe fix. Patch coming soon ...
chris reiss
Comment 8
2011-01-11 08:59:37 PST
bug 18689
appears to be a duplicate of this one.
Alexey Proskuryakov
Comment 9
2011-01-11 09:08:12 PST
> (not 150, sorry.)
Why not 150?
chris reiss
Comment 10
2011-01-11 09:25:27 PST
150 was a miscalculation on my part before i went into the Gecko source. The Gecko limit is given here : content/html/document/src/nsHTMLDocument.cpp:151 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
chris reiss
Comment 11
2011-01-14 14:50:30 PST
Created
attachment 79006
[details]
defense against infinite recursion in document.write( )
WebKit Review Bot
Comment 12
2011-01-14 14:53:36 PST
Attachment 79006
[details]
did not pass style-queue: Failed to run "['Tools/Scripts/check-webkit-style', '--diff-files', u'LayoutTests/dom/html/level2/html/selfRepli..." exit_code: 1 Source/WebCore/dom/Document.cpp:2251: Missing spaces around == [whitespace/operators] [3] Source/WebCore/dom/Document.cpp:2251: Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons. [readability/comparison_to_zero] [5] Source/WebCore/dom/Document.cpp:2252: Missing spaces around = [whitespace/operators] [4] Source/WebCore/dom/Document.h:1397: Extra space between int and m_writeRecursionDepth [whitespace/declaration] [3] Total errors found: 4 in 4 files If any of these errors are false positives, please file a bug against check-webkit-style.
Adam Barth
Comment 13
2011-01-14 17:05:32 PST
Comment on
attachment 79006
[details]
defense against infinite recursion in document.write( ) View in context:
https://bugs.webkit.org/attachment.cgi?id=79006&action=review
> WebCore/dom/Document.cpp:214 > +#define MAX_WRITE_RECURSION_DEPTH 20
We usually use constants instead of pre-processor macros.
> WebCore/dom/Document.cpp:425 > + , m_tooDeepWriteRecursion(false) > + , m_writeRecursionDepth(0)
Why do we have both of these variables? One would seem to be sufficient.
> WebCore/dom/Document.cpp:2222 > + // Prevent DOS attack from self-replicating javascript which causes infinite recursion here
This comment isn't necessary.
> WebCore/dom/Document.cpp:2233 > + if (m_tooDeepWriteRecursion) { > + m_writeRecursionDepth--; > + return; > + } else { > + m_writeRecursionDepth++; > + }
This logic doesn't make much sense to me. Typically for these recursion-depth counts, we use a small class that increments the counter on construction and decrements the counter on destruction. That way we know that we never leak a count. There are examples of how to do this in the HTML parser related to the script nesting level.
chris reiss
Comment 14
2011-01-17 10:36:46 PST
Thanks for the quick feedback, i'll rework this to use const and a counter class. As for the second variable, m_tooDeepWriteRecursion, and the odd logic here -
> + if (m_tooDeepWriteRecursion) { > + m_writeRecursionDepth--; > + return; > + } else { > + m_writeRecursionDepth++; > + }
these were counterintuitive to me too. My first cut at fixing this just returned one level up when m_writeRecursionDepth > MAX_WRITE_RECURSION_DEPTH. This didn't fix the crash. I noticed that Gecko's fix included extra logic to 'panic' all the way up the stack the moment the recursion depth is exceeded. (see
https://bug197052.bugzilla.mozilla.org/attachment.cgi?id=293907
.) After a bit of research I found the javascript self-replication actually makes a tree of recursive calls like so - The first time through Script A appends an extra copy of itself : Script-A --> Script-A + Script-A --> 4 * Script-A and so on. So just bouncing up one level still allows a million or so copies to replicate. Since the recursion is depth-first, Gecko takes the approach if the recursion depth has been exceeded, keep returning all the way up the stack.
Adam Barth
Comment 15
2011-01-17 13:00:02 PST
I mean, there's no way to stop this crash in general. Is this a problem in the real world or only in pathological cases?
chris reiss
Comment 16
2011-01-17 13:34:35 PST
Not a real-world web page; but the bug makes for an easy DOS attack (with one line of javascript code, a page can be created which hangs and crashes any webkit based browser.) Updated the patch just now, i'm pretty sure this will handle any simple case of recursive document.write( ) 's. However, if a person is aware of the max-recursion-depth, it may be possible to come up with a somewhat more complicated variant ...
Adam Barth
Comment 17
2011-01-17 13:40:07 PST
(In reply to
comment #16
)
> Not a real-world web page; but the bug makes for an easy DOS attack (with one line of javascript code, a page can be created which hangs and crashes any webkit based browser.)
There are plenty of other one-liners that do the same thing. There's no way for us to remove them all.
chris reiss
Comment 18
2011-01-17 13:55:57 PST
Created
attachment 79213
[details]
updated fix to infinite recursion in document.write( )
WebKit Review Bot
Comment 19
2011-01-17 13:57:49 PST
Attachment 79213
[details]
did not pass style-queue: Failed to run "['Tools/Scripts/check-webkit-style', '--diff-files', u'LayoutTests/dom/html/level2/html/selfRepli..." exit_code: 1 Source/WebCore/dom/Document.cpp:2248: Tab found; better to use spaces [whitespace/tab] [1] Source/WebCore/dom/Document.cpp:2250: Tab found; better to use spaces [whitespace/tab] [1] Total errors found: 2 in 4 files If any of these errors are false positives, please file a bug against check-webkit-style.
chris reiss
Comment 20
2011-01-17 14:07:44 PST
Created
attachment 79215
[details]
updated fix w/ tabs(!) removed
WebKit Review Bot
Comment 21
2011-01-17 14:17:10 PST
Attachment 79213
[details]
did not build on chromium: Build output:
http://queues.webkit.org/results/7548176
Andreas Kling
Comment 22
2011-01-17 14:18:37 PST
Comment on
attachment 79215
[details]
updated fix w/ tabs(!) removed View in context:
https://bugs.webkit.org/attachment.cgi?id=79215&action=review
This patch needs a ChangeLog, please see
http://trac.webkit.org/wiki/QtWebKitContrib
for more information on how to make patches. Also, there are a number of coding style violations and unnecessary whitespace changes, please fix those for the next iteration.
> WebCore/dom/Document.h:22 > - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, > + * the Free Software Foundation, Inc.,killed myspace 51 Franklin Street, Fifth Floor,
Not sure what's going on here ;)
Alexey Proskuryakov
Comment 23
2011-01-17 14:20:04 PST
Comment on
attachment 79215
[details]
updated fix w/ tabs(!) removed View in context:
https://bugs.webkit.org/attachment.cgi?id=79215&action=review
It seems fine to fix this specific case that Firefox gets right, but I'll let it for others (Adam) to decide. The below are just some style nits. Please do add ChangeLogs:
http://webkit.org/coding/contributing.html
> WebCore/dom/Document.h:22 > + * the Free Software Foundation, Inc.,killed myspace 51 Franklin Street, Fifth Floor,
I didn't know that!
> WebCore/dom/Document.h:1383 > + bool m_tooDeepWriteRecursion;
For better readability, "m_writeRecursionIsTooDeep".
> WebCore/dom/Document.h:1384 > + unsigned int m_writeRecursionDepth;
We just use "unsigned", not "unsigned int".
> WebCore/dom/Document.cpp:100 > +#include "NestingLevelIncrementer.h" > #include "MediaQueryList.h"
Includes should be in alphabetical order.
> WebCore/dom/Document.cpp:215 > +static const int cMaxWriteRecursionDepth= 20;
There shouldn't be two spaces between const and int, and there should be a space before "=".
> WebCore/dom/Document.cpp:2226 > + m_tooDeepWriteRecursion = (m_writeRecursionDepth>1) && m_tooDeepWriteRecursion; > + m_tooDeepWriteRecursion = (m_writeRecursionDepth>cMaxWriteRecursionDepth) || m_tooDeepWriteRecursion;
There should be spaces around ">".
> WebCore/dom/Document.cpp:2242 > if (!hasInsertionPoint && m_ignoreDestructiveWriteCount) > return; > > + > if (!hasInsertionPoint) > open(ownerDocument);
Why did you add this empty line?
> WebCore/dom/Document.cpp:2250 > -#endif > +#endif
You removed one space, which is fine, but why not the other three?
> LayoutTests/dom/html/level2/html/selfReplicatingJS.html:6 > +<script type='text/javascript' src='selfhtml.js'></script><script type='text/javascript'>function loadComplete() { startTest(); }</script></HEAD>
Please don't add new tests to LayoutTests/dom, and use an appropriate subdirectory of LayoutTests/fast/dom instead. These are imported W3C tests.
WebKit Review Bot
Comment 24
2011-01-17 14:29:14 PST
Attachment 79215
[details]
did not build on chromium: Build output:
http://queues.webkit.org/results/7595185
chris reiss
Comment 25
2011-01-19 11:24:10 PST
Created
attachment 79455
[details]
multiple style fixes, layout tests removed, changelogs updated
chris reiss
Comment 26
2011-01-24 14:11:15 PST
Created
attachment 79975
[details]
fix for document write recursion Changed max recursion depth by 1 so output exactly matches that of Firefox.
chris reiss
Comment 27
2011-01-24 14:20:14 PST
To summarize, this patch is the same logic as in FireFox patch
https://bug197052.bugzilla.mozilla.org/attachment.cgi?id=293907
in bug
https://bugzilla.mozilla.org/show_bug.cgi?id=197052
. This latest tweak corrects an off-by-one count so that webkit's output matches that of firefox. IE also handles this special case, but with a lower max recursion depth.
chris reiss
Comment 28
2011-01-25 10:23:24 PST
Created
attachment 80078
[details]
same as above, updated to merge cleanly
Adam Barth
Comment 29
2011-01-25 21:29:25 PST
Comment on
attachment 80078
[details]
same as above, updated to merge cleanly View in context:
https://bugs.webkit.org/attachment.cgi?id=80078&action=review
> Source/WebCore/dom/Document.cpp:105 > +#include "NestingLevelIncrementer.h"
Technically we shouldn't be including headers from the parser directory in other parts of the project. Ideally we'd move this class to WebCore/platform or to WTF, but I'm not going to make you do that in this patch. It would be nice to do as a follow-up patch though.
> Source/WebCore/dom/Document.cpp:2256 > + NestingLevelIncrementer nestingLevelIncrementer(m_writeRecursionDepth);
I worry that the parser can destroy the document and then we'll be twiddling unallocated memory in this object's destructor. If you look inside the parser, you'll see that it refs the document in some cases. Some of the comments there or the svn blame for when those refs were added should give you some ideas for how to write a test to see this issue.
> Source/WebCore/dom/Document.cpp:2259 > + m_writeRecursionIsTooDeep = (m_writeRecursionDepth > 1) && m_writeRecursionIsTooDeep; > + m_writeRecursionIsTooDeep = (m_writeRecursionDepth > cMaxWriteRecursionDepth) || m_writeRecursionIsTooDeep;
It's slightly strange that after we panic to the top of the stack m_writeRecursionIsTooDeep is still true even though there's not recursing on the stack. m_writeRecursionIsTooDeep only gets cleared when write is finally called again with a clean stack. I'm not sure I see a clean way of clearing the variable though...
> Source/WebCore/ChangeLog:7 > + Self-replicating code makes Safari hang and eventually crash > +
https://bugs.webkit.org/show_bug.cgi?id=15123
> +
It would be nice to explain more about why we're making this change. For example, I would mention that we're trying to match Firefox, which is where these values and the "panic to the top of the stack" behavior come from.
Adam Barth
Comment 30
2011-01-25 21:30:34 PST
See HTMLDocumentParser::insert for the ref() issue.
Adam Barth
Comment 31
2011-01-25 21:31:52 PST
Here's the change where those refs were added:
http://trac.webkit.org/changeset/65692
We should test patterns like the ones tested in that patch together with this panic / unwinding code.
chris reiss
Comment 32
2011-01-28 11:03:14 PST
So if I understand you correctly, what's needed is this guard code : RefPtr<Document> protect(this); { NestingLevelIncrementer nestingLevelIncrementer(m_writeRecursionDepth); m_writeRecursionIsTooDeep = (m_writeRecursionDepth > 1) && m_writeRecursionIsTooDeep; m_writeRecursionIsTooDeep = (m_writeRecursionDepth > cMaxWriteRecursionDepth) || m_writeRecursionIsTooDeep; if (m_writeRecursionIsTooDeep) return; } and then essentially the same tests as for
http://trac.webkit.org/changeset/65692
?
Adam Barth
Comment 33
2011-01-28 11:43:54 PST
(In reply to
comment #32
)
> So if I understand you correctly, what's needed is this guard code :
It's a question. It might be the case that the parser gets blown away but the document remains. In any case, we should add the tests. If the test pass then we don't need to change the code. If the tests crash, we'll want to not crash. :)
chris reiss
Comment 34
2011-01-28 13:09:22 PST
Created
attachment 80488
[details]
write recursion fix added tests (non-crashing) and explanation to change-log.
Adam Barth
Comment 35
2011-01-28 13:56:22 PST
Comment on
attachment 80488
[details]
write recursion fix View in context:
https://bugs.webkit.org/attachment.cgi?id=80488&action=review
Thanks for adding the extra tests!
> LayoutTests/fast/dom/Document/document-close-iframe-load.html:6 > +<!--iframe onload="document.open();document.close();" -->
Is there a reason this is commented out? This test doesn't seem like it tests very much...
> LayoutTests/fast/dom/Document/document-close-iframe-load-expected.txt:2 > +ALERT: PASS (just to make expected.txt non-empty) > +
I'm not sure how this expected file gets generated here.
chris reiss
Comment 36
2011-01-28 15:21:28 PST
you're right, that shouldn't be commented out, shall i patch on top of it once this lands to correct that? (i did test with the comments removed!)
Adam Barth
Comment 37
2011-01-28 15:22:52 PST
(In reply to
comment #36
)
> you're right, that shouldn't be commented out, shall i patch on top of it once this lands to correct that? (i did test with the comments removed!)
I'd just upload a new version of the patch. The commit-queue should reject the patch if the test fails. :)
WebKit Commit Bot
Comment 38
2011-01-29 02:28:00 PST
Comment on
attachment 80488
[details]
write recursion fix Rejecting
attachment 80488
[details]
from commit-queue. Failed to run "['./Tools/Scripts/webkit-patch', '--status-host=queues.webkit.org', '--bot-id=cr-jail-8', 'build-..." exit_code: 2 Last 500 characters of output: .................................................................................................................... fast/dom/Attr .... fast/dom/CSSStyleDeclaration ..... fast/dom/DOMException .... fast/dom/DOMImplementation ..... fast/dom/Document ..... fast/dom/Document/document-close-iframe-load.html -> failed Exiting early after 1 failures. 7141 tests run. 150.05s total testing time 7140 test cases (99%) succeeded 1 test case (<1%) had incorrect layout 4 test cases (<1%) had stderr output Full output:
http://queues.webkit.org/results/7506476
chris reiss
Comment 39
2011-01-31 09:27:22 PST
Created
attachment 80647
[details]
write recursion fix Fixed layout test ...
chris reiss
Comment 40
2011-01-31 09:32:42 PST
Created
attachment 80648
[details]
write recursion fix fixed Layout test
Eric Seidel (no email)
Comment 41
2011-01-31 16:05:53 PST
Comment on
attachment 80488
[details]
write recursion fix Cleared Adam Barth's review+ from obsolete
attachment 80488
[details]
so that this bug does not appear in
http://webkit.org/pending-commit
.
WebKit Commit Bot
Comment 42
2011-01-31 23:12:27 PST
Comment on
attachment 80648
[details]
write recursion fix Rejecting
attachment 80648
[details]
from commit-queue. Failed to run "['./Tools/Scripts/webkit-patch', '--status-host=queues.webkit.org', '--bot-id=cr-jail-8', 'build-..." exit_code: 2 Last 500 characters of output: ................................................................................................................. fast/dom/Attr .... fast/dom/CSSStyleDeclaration ..... fast/dom/DOMException .... fast/dom/DOMImplementation ..... fast/dom/Document .......... fast/dom/Document/document-write-recursion.html -> failed Exiting early after 1 failures. 7149 tests run. 150.76s total testing time 7148 test cases (99%) succeeded 1 test case (<1%) had incorrect layout 4 test cases (<1%) had stderr output Full output:
http://queues.webkit.org/results/7685464
chris reiss
Comment 43
2011-02-01 14:22:22 PST
Created
attachment 80827
[details]
write recursion fix Another test fix. Apologies to adam!
WebKit Commit Bot
Comment 44
2011-02-01 16:23:04 PST
Comment on
attachment 80827
[details]
write recursion fix Clearing flags on attachment: 80827 Committed
r77333
: <
http://trac.webkit.org/changeset/77333
>
WebKit Commit Bot
Comment 45
2011-02-01 16:23:13 PST
All reviewed patches have been landed. Closing bug.
Adam Barth
Comment 46
2011-02-01 16:29:08 PST
Yay~
Adam Barth
Comment 47
2011-02-03 09:33:55 PST
***
Bug 18689
has been marked as a duplicate of this bug. ***
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug