45.93 KB, text/plain
Steps to reproduce: 1. Go to http://www.squarefree.com/spampede/evil.html. 2. Click the "Try to crash" button at the top. Result: Firefox will either hang or crash [@ SetEmptyRgn], with about equal probability. Firefox build: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.9a1) Gecko/20050918 Firefox/1.6a1 Java version used by Firefox: 1.4.2_09 from Apple Mac OS X version: 10.4.2
Thread 0 Crashed: com.apple.QD 0 SetEmptyRgn + 24 com.netscape.MRJPlugin 1 MRJContext::synchronizeClipping() + 296 2 MRJContext::setWindow(nsPluginWindow*) + 428 3 MRJPluginInstance::SetWindow(nsPluginWindow*) + 88 org.mozilla.firefox 4 MOZ_Z__length_code + 197856 5 nsPluginHostImpl::InstantiateEmbeddedPlugin(char const*, nsIURI*, nsIPluginInstanceOwner*) + 2152 6 nsObjectFrame::InstantiatePlugin(nsPresContext*, nsHTMLReflowMetrics&, nsHTMLReflowState const&, nsIPluginHost*, char const*, nsIURI*) + 908 7 nsObjectFrame::Reflow(nsPresContext*, nsHTMLReflowMetrics&, nsHTMLReflowState const&, unsigned&) + 2024
I get crashes and hangs (after clicking "Try to crash") with all the Mozilla.org browsers and JEP versions that I tried. I also got crashes without the JEP (using Java 1.3.1 via Apple's Java Applet.plugin). The crashes were all over the map, though they were ususally in JVM code. I never saw a crash in QD's SetEmptyRgn(). All my tests were on OS X 10.4.2. But I'm sure these results (or something like them) could be replicated on OS X 10.3.X and 10.2.8. Safari didn't crash. Mozilla 1.7.11 with Sun's Java 1.4.2_08 did't crash on SuSE Linux 9.2. I'm not sure there's anything I can do about this. I think it has something to do with the fact that the Mozilla.org browsers (using the OJI) expect an applet to be created synchronously -- a call is made to nsIPluginInstance::setWindow(), and an applet is just supposed to be up and running by the time it returns. But in Apple's implementations of Sun's "recent" JVMs, most important procedures (like starting the JVM or loading an applet) take place asynchronously -- especially in Java 1.4.X and 1.5, but even (to some extent) in Java 1.3.1. There's lots of code in JavaEmbeddingPlugin.bundle (and even some in MRJPlugin.plugin) that tries to compensate for this (by for example waiting for a certain stage to be reached in loading an applet (while processing NSRunLoop and CFRunLoop events on the main thread) before returning from the browser's call to setWindow()). But my ability to compensate is never going to be perfect. I suspect that the reason Safari doesn't crash is that it starts the JVM and loads applets asynchronously -- i.e. it starts these procedures in a new thread and uses a callback to find out when they've finished. But I don't know why Mozilla 1.7.11 on Linux with Sun's Java 1.4.2_08 doesn't crash. Maybe someone who knows more about Sun's code can enlighten us.
I should add that this test case (the "Try to crash" button) isn't a real world example -- I don't think that applets will ever be instantiated (and deinstantiated) so rapidly in normal usage. I've tried hard to make the JEP behave correctly when switching rapidly (using the back and forward buttons) between three or four browser pages, each of which contains lots of applets. Examples of such pages can be found at the following URLs. http://www.mozilla.org/quality/browser/front-end/testcases/oji/tagstest.html http://brittnysseafood.com/ The applets at brittnysseafood are the most fiendish torture I've yet found for the JEP :-)
This may not be real-world behavior, but crashes on these kinds of evil pages make it difficult for me to test for a class of security holes (see bug 306663).
Executing the testcase with Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4) Gecko/20050916 SeaMonkey/1.0a with Java 1.5.0_02 the memory consumption rises asymptotical up to 150 MB. After closing the window with the testcase, SeaMonkey is partially freezed (e.g. no autofocus to address bar after opening new tab, no autocompletion to URIs etc.) and sometimes can't be quit without killing the process in task manager. Another oddity is, that the testcase can't be executed a second time when above point is reached.
> The crashes were all over the map, though they were ususally in JVM code. I > never saw a crash in QD's SetEmptyRgn(). I later did some tests on Mac OS X 10.3.9 (with jep.asyncinit turned on), and did see some examples of your crash in QuickDraw's SetEmptyRgn() (called from MRJPlugin.plugin). These must have resulted from trying to access MRJContext objects after they had been deleted, which must (in turn) have been caused by the browser making calls to nsIPluginInstance::setWindow() on a plugin instance after having already called nsIPluginInstance::destroy() on the same instance. I already had code in place (in the MRJ Plugin) to deal with this kind of eventuality. I've tightened this code up, and the crashes in SetEmptyRgn() seem to have disappeared. The improved code appears in version 0.9.4+a of the Java Embedding Plugin (a so-called "nightly"), which I've just released: http://javaplugin.sourceforge.net/
Chris - can you help us get this bug better assigned?
If I understand things right smichaud is a good owner, and we just need to take a drop in the mozilla tre from http://javaplugin.sourceforge.net/ when the nightlies that smichaud has tested all check out; then the fix will show up camino and firefox builds. smichaud, asa, others, does that sound right? chris h.
Assignee: pete.zha → smichaud
This problem is, practically speaking, insoluble. For more information see comment #2. That said, I've actually been able to use Jesse's "evil" applet to track down and fix problems in the MRJ Plugin JEP (see comment #7) and JavaEmbeddingPlugin.bundle. These changes have appeared in subsequent JEP versions ... though most of them are already present in JEP 0.9.5 (what's bundled with Firefox 1.5). (The latest JEP version is 0.9.5+b.) However, you need to be aware (and I'm repeating this from earlier comments) that the "evil" applet (in my experience) only provides useful results if you set "jep.anyncinit" to "true" -- otherwise you get crashes all over the map. And this setting isn't compatible with everyday use of the JEP (see comment #6).
I'd like to leave this problem open but downgrade it from "critical" to "minor". Is that OK with everyone?
Status: NEW → ASSIGNED
Firefox trunk, which comes with JEP 0.9.5+b, usually becomes unresponsive and crashes if I try to quit. I don't need to force quit. Example crash: Thread 23 Crashed: 0 _class_lookupMethodAndLoadCache + 376 1 objc_msgSend + 244 2 Java_MyCAppletFrame_getViewPtr + 35412 3 Java_MyCAppletFrame_getViewPtr + 34624 4 forkThreadForFunction + 108 5 _pthread_body + 96 Firefox 1.5, which comes with JEP 0.9.5, behaves similarly. Example crash: Thread 20 Crashed: 0 objc_msgSend + 32 1 Java_MyCAppletFrame_getViewPtr + 35412 2 Java_MyCAppletFrame_getViewPtr + 33444 3 Java_MyCAppletFrame_getViewPtr + 34216 4 forkThreadForFunction + 108 5 _pthread_body + 96 I also saw Firefox 1.5 crash in SetEmptyRgn once. Is it just a bug that Gecko calls nsIPluginInstance::setWindow() on a plugin instance after having already called nsIPluginInstance::destroy() on the same instance (comment 7), or is that a result of the same design decisions that make it difficult to fix the other crashes? Are any of these crashes likely to be exploitable, or do they crash in predictable, safe ways (e.g. reading memory address zero)?
Did you set jep.asyncinit to true before you did this test? If not, then your crashes may not mean a whole lot -- as I pointed out in comment #2 and comment #10. Whenever I tried your "evil" applet without this setting, I got crashes all over the place. That said, the particular traces you've given look like attempts to send Objective C "messages" to objects that have been deleted.
> That said, the particular traces you've given look like attempts to send > Objective C "messages" to objects that have been deleted. My guess would be that this kind of error isn't exploitable.
> I also saw Firefox 1.5 crash in SetEmptyRgn once. Is it just a bug that > Gecko calls nsIPluginInstance::setWindow() on a plugin instance after having > already called nsIPluginInstance::destroy() on the same instance (comment > 7), or is that a result of the same design decisions that make it difficult > to fix the other crashes? As I've already said in comment #7, I saw this crash pretty consistently on OS X 10.3.9 with jep.asyncinit set to true, in a (now) old version of the JEP. It was due to Gecko calling nsIPluginInstance::setWindow() on a deleted object, and I've now worked around the problem.
> This problem is, practically speaking, insoluble. For more information see > comment #2. The problem _would_ be soluble if mozilla.org were willing to change how its browsers launch applets on OS X -- for example waiting for an "applet loaded" callback before trying to use a Java applet.
is this mac only? we should be able to avoid calling stuff in destroyed objects w/o needing async plugin instation. of course, if it's not mozilla which does that, then this isn't really mozilla's fault...
> is this mac only? Yes, I believe so. > we should be able to avoid calling stuff in destroyed objects w/o needing > async plugin instation ... Asynchronous loading (the "applet loaded callback") isn't aimed at solving this problem (which in any case is pretty easily worked around), but at making Mozilla.org browsers compatible with how Apple's recent JVMs (1.4.X and 5.0) load applets. These newer JVMs need to load applets on a secondary thread. And (of course) there might be several seconds' delay before an applet is loaded and initialized. But mozilla.org browsers currently expect a Java applet to be fully loaded and initialized by the time a call (on the main thread) to nsIPluginInstance::setWindow() (the one which triggers loading the applet) returns. The Java Embedding Plugin currently deals with this by processing events on the main thread (in its own event loop) until the applet seems to be ready, and only then allowing the relevant call to nsIPluginInstance::setWindow() to return. This works fine in ordinary circumstances. But it goes haywire when applets are loaded and destroyed too rapidly. It seems that the JEP's event loop processing falls behind when this happens, leading to massive numbers of calls to objects that have been deleted (and not just in JEP code). But the JEP's event loop processing would be unnecessary if mozilla.org browsers didn't expect a Java applet to be fully initialized after nsIPluginInstance::setWindow() returned -- e.g. if it they supported an "applet loaded" or "applet initialized" callback. For another discussion of these issues, see: https://bugzilla.mozilla.org/show_bug.cgi?id=311530#c2
so er, why is it not sufficient to just cache the arguments of the last SetWindow call and pass them on to java whenever it's ready?
The arguments to SetWindow don't matter in this case (they're basically irrelevant). What does matter is that an applet gets launched whenever a call to SetWindow is made whose "nsIPluginWindow *pluginWindow" argument is non-NULL and the plugin's applet hasn't yet been launched. See MRJContext::setWindow() in the MRJ Plugin JEP's plugin/oji/MRJCarbon/plugin/Source/MRJContext.cp. The same code (or or less) is also present in the original MRJ Plugin.
And even if the arguments to SetWindow were relevant, caching them wouldn't help. The problem won't be solved by delaying launching the applet.
Steven, what are the relevant calls in Gecko that are expecting stuff to be initialized before it has been?
We can block onload handler firing on applet init if you can communicate back to layout that init is not done yet, and when it completes. Alternately, if the JEP has access to an nsIDocument it can do so itself...
> We can block onload handler firing on applet init if you can communicate > back to layout that init is not done yet, and when it completes. Currently the JEP C/C++ API supports three callbacks: appletCreatedCallback (usually called on a secondary thread), appletInitializedCallback (currently always called on a secondary thread) and appletStartedCallback (currently always called on the main thread). (For more info see JavaEmbeddingPlugin.h.) What code might I put in an appletInitializedCallback? Would it be alright to make these calls (whatever they are) on a secondary thread? (If the answer is "no", I could "schedule" a block of code to be called on the main thread ... though that could mean that the code doesn't get run as soon as it should.) Whatever objects and methods get used here, they should be accessible (directly or indirectly) via calls to the nsIServiceManager interface. > Alternately, if the JEP has access to an nsIDocument it can do so itself... I'm pretty sure the JEP (i.e. the MRJ Plugin JEP) doesn't have access to this.
Hmmm... So what we need is a way to get to the right document (the one the applet is for). Or at least get to the right nsIPluginTagInfo(2?) or something. That can find a document, and we could add an interface to it that has methods you could call to block and unblock onload.
> Or at least get to the right nsIPluginTagInfo(2?) or something. I didn't know about this ... thanks! You can get it from an nsIPluginInstancePeer instance, a pointer to which is provided as a parameter to nsIPluginInstance::Initialize() (which is already implemented in MRJPlugin.cpp). > That can find a document, nsIPluginTagInfo2 has an nsIDOMElement attribute. Is this what you're talking about? > and we could add an interface to it that has methods you could call to block > and unblock onload. This would (presumably) take care of the onload handler problem. But there are also more subtle manifestations of the "browser uses applet before it's ready" problem. One example is an applet which (like the NOAA radar looping applets) updates the screen during its init() method (i.e. before initialization has been completed) -- the NOAA radar looping applet downloads its "maps" in its init() method, and draws each one as it has finished downloading. The JEP handles this correctly with its "synchronizing" workaround in place. But if "jep.asyncinit" is set to "true", no map gets drawn until the applet's init() method has finished. I'm not entirely sure why this happens. I'm also quite sure I could find other examples of mozilla.org browsers wanting to use a Java applet before it's ready on OS X with the JEP and "jep.asyncinit" set to "true". Nonetheless, I think it's worthwhile trying to find a solution to the onload handler problem, even if our solution won't resolve the whole class of "browser uses applet before it's ready" problems. As best I can tell, the onload handler problem is the most glaring (and serious) of the lot.
> nsIPluginTagInfo2 has an nsIDOMElement attribute. Is this what you're talking > about? More or less. It can get to the document via the element. I'd rather add methods on an nsIPluginTagInfo3 interface of some sort than make JEP depend on nsIDocument. ;)
Is this bug something that should be fixed in the JEP (and thus should live in Core:Java Embedding Plugin) or somewhere else in Gecko (or perhaps both)?
WFM, Mac trunk debug + Mac OS X 10.4.11. I don't see a crash or hang. I do see some assertions, but I think they're covered in other bug reports. ###!!! ASSERTION: wrong thread: 'NS_IsMainThread()', file /Users/jruderman/trunk/mozilla/netwerk/base/src/nsIOService.cpp, line 477 ###!!! ASSERTION: nsCookieService not thread-safe: '_mOwningThread.GetThread() == PR_GetCurrentThread()', file /Users/jruderman/trunk/mozilla/netwerk/cookie/src/nsCookieService.cpp, line 405 ###!!! ASSERTION: nsPluginHostImpl not thread-safe: '_mOwningThread.GetThread() == PR_GetCurrentThread()', file /Users/jruderman/trunk/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp, line 2718
Status: ASSIGNED → RESOLVED
Closed: 11 years ago
Resolution: --- → WORKSFORME
Crash Signature: [@ MRJContext::synchronizeClipping] [@ SetEmptyRgn]
You need to log in before you can comment on or make changes to this bug.