Closed Bug 64589 Opened 24 years ago Closed 24 years ago

Recursive Javascript/DOM additions crash browser HARD

Categories

(Core :: JavaScript Engine, defect)

x86
All
defect
Not set
normal

Tracking

()

VERIFIED DUPLICATE of bug 56940
mozilla1.0

People

(Reporter: chris, Assigned: rogerl)

References

()

Details

(Keywords: crash, testcase, Whiteboard: EVIL)

Attachments

(6 files)

The dozen line recursive script found here[1] crashes mozilla big time [98/NT4 & Mac]. It has brought down the entire system when tried on the Mac side. I don't know if its the recursion, or the creation of a giant document, but this scenario should be handled much better. It's just too easy for a malicious script to take out the browser/OS. [1] http://www.assembler.org/AX/bomb.html - but you might want to use this instead - view-source:http://www.assembler.org/AX/bomb.html
On Mac OS 9.0.4 build 2001010508 their is no crash. Reporter, which Mac build r u using? Clicking on "kill-9" as the script is called results in nothing happening. The browser window is blank, and their is no crash or freeze of any kind.
I don't know what build it was on the Mac side that did this as that info came from the author of that page and not me. I will try and get some more info early this week (next time I talk to the author). On the PC (win98/ build 2001010708) side I can confirm it is still an issue.
Confirmed Platform: PC OS: Linux 2.2.16 Mozilla: 2001010512 Marking NEW. (although I believe this is a duplicate)
Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: crash, testcase
for the record, iepxlore ate 98% of my cpu on this wonderful link. the remaining 2% was enough to nice the process (it's now using 80% or whatever it can get while i happilly use my computer). you could consider this to be a dupe of browser should be responsive in most infinte loops. or a dupe of the halting problems. I just watch as memory usage slowly climbs (<22mb ram, and only 13mb vm). oh, and this page actually crashed mdm.exe when i started the debugger (before having clicked the link) [Microsoft Debug Manager - handles the javascript errors]
Assignee: asa → rogerl
Component: Browser-General → Javascript Engine
QA Contact: doronr → pschwartau
Whiteboard: EVIL
JS protects itself against infinite recursion, eventually -- or did last time I checked. The engine will hog cycles until it hits a recursion stopper (either the branch callback limit, or the internal interpreter-recursion-level counter limit, or the iterative-call-count limit -- see http://lxr.mozilla.org/mozilla/source/js/src/jsinterp.c#1137 and below). But AFAICT, JS isn't running enough to hit its runaway-limiters. Here's a stack fragment I got in gdb: #0 nsCOMPtr<nsIStyleContext>::get (this=0xbffdf0d8) at ../../../../dist/include/nsCOMPtr.h:630 #1 0x418547ed in nsCSSFrameConstructor::CreateAnonymousFrames ( this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aTag=0x8148750, aState=@0xbfffe380, aParent=0x89e4430, aNewFrame=0x87a57f0, aChildItems=@0xbffdf170) at nsCSSFrameConstructor.cpp:5354 #2 0x41854204 in nsCSSFrameConstructor::ConstructFrameByTag (this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aState=@0xbfffe380, aContent=0x89e4430, aParentFrame=0x87a57a4, aTag=0x8148750, aNameSpaceID=0, aStyleContext=0x8895f88, aFrameItems=@0xbffdf470) at nsCSSFrameConstructor.cpp:5212 #3 0x41859b02 in nsCSSFrameConstructor::ConstructFrameInternal ( this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aState=@0xbfffe380, aContent=0x89e4430, aParentFrame=0x87a57a4, aTag=0x8148750, aNameSpaceID=0, aStyleContext=0x8895f88, aFrameItems=@0xbffdf470, aXBLBaseTag=0) at nsCSSFrameConstructor.cpp:7563 #4 0x418595d7 in nsCSSFrameConstructor::ConstructFrame (this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aState=@0xbfffe380, aContent=0x89e4430, aParentFrame=0x87a57a4, aFrameItems=@0xbffdf470) at nsCSSFrameConstructor.cpp:7482 #5 0x4186676b in nsCSSFrameConstructor::ProcessChildren (this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aState=@0xbfffe380, aContent=0x89e43f4, aFrame=0x87a57a4, aCanHaveGeneratedContent=1, aFrameItems=@0xbffdf470, aParentIsBlock=1, aTableCreator=0x0) at nsCSSFrameConstructor.cpp:11417 #6 0x4184e045 in nsCSSFrameConstructor::ConstructTableCellFrame ( this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aState=@0xbfffe380, aContent=0x89e43f4, aParentFrameIn=0x87a56f4, aStyleContext=0x8924bf0, aTableCreator=@0xbffdfbec, aIsPseudo=0, aChildItems=@0xbffdf68c, aNewCellOuterFrame=@0xbffdf528, aNewCellInnerFrame=@0xbffdf514, aIsPseudoParent=@0xbffdf52c) at nsCSSFrameConstructor.cpp:2881 #7 0x4184ec3f in nsCSSFrameConstructor::TableProcessChild (this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aState=@0xbfffe380, aChildContent=@0x89e43f4, aParentFrame=0x87a56f4, aParentFrameType=0x814e458, aParentStyleContext=0x8924858, aTableCreator=@0xbffdfbec, aChildItems=@0xbffdf68c, aCaption=@0xbffdf688) at nsCSSFrameConstructor.cpp:3137 #8 0x4184e808 in nsCSSFrameConstructor::TableProcessChildren (this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aState=@0xbfffe380, aContent=0x89e43b0, aParentFrame=0x87a56f4, aTableCreator=@0xbffdfbec, aChildItems=@0xbffdf68c, aCaption=@0xbffdf688) at nsCSSFrameConstructor.cpp:3048 #9 0x4184db39 in nsCSSFrameConstructor::ConstructTableRowFrame ( this=0x89f0b58, aPresShell=0x89d68e8, aPresContext=0x8a35ba8, aState=@0xbfffe380, aContent=0x89e43b0, aParentFrameIn=0x896c4f8, aStyleContext=0x8924858, aTableCreator=@0xbffdfbec, aIsPseudo=0, aChildItems=@0xbffdf888, aNewFrame=@0xbffdf710, Probably the generated table has gotten horrendously deep. Stopping denial of service attacks is hard precisely because you don't want to deny service to legitimate service-clients. Maybe the table code can be paranoid, especially about generated content coming from the DOM (innerHTML assignment in particular, as in this case). Cc'ing jst, reassigning to karnaze; here's the bad function: var table1 = "<table cellpadding=0 cellspacing=0 border=0><tr><td> "; var table2 = "</td></tr></table>"; var bigOut1 = new Array(); var bigOut2 = new Array(); var content = ""; function bomb() { bigOut1[count] = table1; bigOut2[count] = table2; for (var i = bigOut1.length-1; i>=0; i--) { content = bigOut1[i] + content + bigOut2[i]; } document.getElementById("b0").innerHTML = content; count++; bomb(); } /be
Assignee: rogerl → karnaze
Setting default component -
Component: Javascript Engine → HTMLTables
QA Contact: pschwartau → chrisd
Moving to mozilla0.9.1.
Status: NEW → ASSIGNED
Target Milestone: --- → mozilla0.9.1
QA contact update
QA Contact: chrisd → amar
I didn't see any activity until I went to the url without the "view-source" (and then hit the link). On a debug WinNT build, I let it run for quite a while and every time I broke saw. memset() line 108 _free_dbg_lk(void * 0x0563c8d8, int 1) line 1082 + 28 bytes _free_dbg(void * 0x0563c8d8, int 1) line 970 + 13 bytes free(void * 0x0563c8d8) line 926 + 11 bytes js_FinalizeStringRT(JSRuntime * 0x00da6980, JSString * 0x00dd45b0) line 2346 + 13 bytes js_FinalizeString(JSContext * 0x01345e70, JSString * 0x00dd45b0) line 2335 + 16 bytes js_GC(JSContext * 0x01345e70, unsigned int 1) line 1218 + 11 bytes js_AllocGCThing(JSContext * 0x01345e70, unsigned int 1) line 497 + 11 bytes js_NewString(JSContext * 0x01345e70, unsigned short * 0x09b2a2f0, unsigned int 104583, unsigned int 0) line 2250 + 15 bytes js_Interpret(JSContext * 0x01345e70, long * 0x0012fc88) line 2124 + 25 bytes js_Execute(JSContext * 0x01345e70, JSObject * 0x00dabcc0, JSScript * 0x0136ce70, JSStackFrame * 0x00000000, unsigned int 0, long * 0x0012fc88) line 956 + 13 bytes JS_EvaluateUCScriptForPrincipals(JSContext * 0x01345e70, JSObject * 0x00dabcc0, JSPrincipals * 0x0128d910, const unsigned short * 0x0012fd80, unsigned int 21, const char * 0x00000000, unsigned int 0, long * 0x0012fc88) line 3222 + 25 bytes nsJSContext::EvaluateString(nsJSContext * const 0x01344580, const basic_nsAReadableString<unsigned short> & {...}, void * 0x00dabcc0, nsIPrincipal * 0x0128d90c, const char * 0x00000000, unsigned int 0, const char * 0x00000000, basic_nsAWritableString<unsigned short> & {...}, int * 0x02fafce4) line 609 + 68 bytes nsEvaluateStringProxy::EvaluateString(nsEvaluateStringProxy * const 0x0136c0a0, char * * 0x02fafce0, int * 0x02fafce4) line 167 + 64 bytes XPTC_InvokeByIndex(nsISupports * 0x0136c0a0, unsigned int 4, unsigned int 2, nsXPTCVariant * 0x0136f590) line 139 EventHandler(PLEvent * 0x0136f5e0) line 509 + 41 bytes PL_HandleEvent(PLEvent * 0x0136f5e0) line 588 + 10 bytes PL_ProcessPendingEvents(PLEventQueue * 0x012d0370) line 518 + 9 bytes _md_EventReceiverProc(HWND__ * 0x0002036c, unsigned int 49297, unsigned int 0, long 19727216) line 1069 + 9 bytes USER32! DispatchMessageWorker@8 + 135 bytes USER32! DispatchMessageA@4 + 11 bytes nsNativeViewerApp::Run() line 84 main(int 1, char * * 0x00ba5e60) line 157 + 11 bytes mainCRTStartup() line 338 + 17 bytes KERNEL I was hoping to see a crash, but got the impression I never would since the memory usage went way up (but not enough to run out of VM) and then down. I'm not sure what the status of our memory pressure API is, but if there were a test case that did run out of memory, that would be the solution. Reassigning to js engine component to take a look.
Assignee: karnaze → rogerl
Status: ASSIGNED → NEW
Component: HTMLTables → Javascript Engine
QA Contact: amar → pschwartau
Target Milestone: mozilla0.9.1 → mozilla1.0
These bugs may be of related interest: bug 21762 tracking: poor performance using DHTML bug 65509 Very slow javascript pulldown menu bug 64516 95-99% CPU usage for dhtml "snow effect" bug 40711 Compare rendering performance with IE5
NOTE: the above testcase uses document.getElementById() to display its output, so it will not run in a non-W3C-compliant browser. This testcase only allows the bomb() function to recurse 50 times. Click the "TEST" link, and wait a few seconds for the test to complete. I made it to show how the double looping in the bomb() function looks. It also shows that the variable "content" grows in length by a fixed amount on each iteration of the double loop; namely, 71. This represents the sum of the 53 characters contained in the the string variable "table1", and the 18 characters contained in the variable "table2", which get concatenated to the content variable at each iteration. NOTE: in the testcase, I DELETED this line from the bomb() function: document.getElementById("b0").innerHTML = content; So now it only has to do with string concatenation, as far as I can tell. I have the UBound on recursion set to 50. If you want to experiment, save the testcase locally and change the UBound variable. Watch out!!! If it's set to 100, for example, we run into the memory problems in the original report. With a high UBound you may also need to increase the rows attribute in the <TEXTAREA> element... Since the growth of the content string is linear, this bug differs from bug 40391, "JS + on long strings freezes mozilla, horks win98". In that bug's testcase, the string lengths grow exponentially. Perhaps this bug is related to bug 56940: "O(n**2) and O(n**3) growth too easy with JS string concat" - ?
Unless we need another bug on DOM problems, I think this is a dup of bug 56940. Phil, what do you say? Mark dup if you agree. /be
Even with these simplifed JS Shell testcases, we see the same behavior as originally reported in the bug. Here, of course, the DOM plays no role. I also had to convince myself that the Array() objects were not the problem; hence the second JS Shell testcase where they have been removed. Here is the bomb() function from this last testcase: function bomb() { for (i=count; i>=0; i--) { content = table1 + content + table2; j++; } if (++count < UBound) bomb(); } Here, the variable j is just keeping track of total loop iterations. The variables "table1", "table2" are same strings as in the original testcase at the give URL above: table1 = '<table cellpadding=0 cellspacing=0 border=0><tr><td> '; table2 = '</td></tr></table>'; Thus, in this last incarnation of the test, at each iteration we are simply doing string concatenation of global variables: content = table1 + content + table2 We run out of memory as originally reported in this bug -
If you look at the test results, you will see little difference between the two testcases. We run out of memory at just about the same spot. It is interesting to look at the relationship between the time to completion and the total number of iterations. For example, from the second test: js> test(20) UBound on bomb() recursion is set to 20 time = 46ms Total number of iterations = 210 content.length = 14910 js> test(30) UBound on bomb() recursion is set to 30 time = 203ms Total number of iterations = 465 content.length = 33015 Since we have eliminated DOM objects from the test, and Array objects, I suppose there is only one further issue aside from string concatenation: does this bug reveal something inefficient about function recursion in JS? I agree with Brendan's opinion at 2001-03-25 15:55 above, and will mark this as a duplicate of bug 56940, "O(n**2) and O(n**3) growth too easy with JS string concat" *** This bug has been marked as a duplicate of 56940 ***
Status: NEW → RESOLVED
Closed: 24 years ago
Resolution: --- → DUPLICATE
The results from the third and simplest JS shell testcase above are virtually the same as from the first and second. We run out of memory at roughly the same spot. Here is the bomb() function this time: function bomb() { for (count=0; count<UBound; count++) { for (i=count; i>=0; i--) { content = table1 + content + table2; j++; } } } In this version, there are none of these features of the original testcase: 1. DOM objects 2. Array objects 3. Function recursion We have reduced the testcase to loop iteration and string concatenation. Since the results have been materially the same every time, this provides further evidence that the issue in this bug is string concatenation -
Status: RESOLVED → VERIFIED
A testcase for this bug was already added in the original bug (bug 56940).
Flags: in-testsuite-
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: