Closed
Bug 145054
Opened 22 years ago
Closed 22 years ago
Plugins may starve when cached data they read from is removed too soon
Categories
(Core Graveyard :: Plug-ins, defect, P3)
Tracking
(Not tracked)
RESOLVED
FIXED
mozilla1.2alpha
People
(Reporter: billyboy, Assigned: srgchrpv)
References
()
Details
(Whiteboard: [PL2:NA][plugger])
Attachments
(2 files, 3 obsolete files)
18.73 KB,
patch
|
Details | Diff | Splinter Review | |
28.23 KB,
patch
|
serhunt
:
review+
darin.moz
:
superreview+
|
Details | Diff | Splinter Review |
I'm viewing a page which has this simple html code: <embed src="home.mid" width=200 height=55 autostart=true>. Plugger calls timidity with $file == "/tmp/plugtmp/home.mid" properly but plugger hangs. Strace'ing mozilla reveals that it: 1- mkdir("/tmp/plugtmp"); 2- Writes the content of the midi file into /tmp/plugtmp/home.mid 3- forks the plugger 4- stat, lstat and unlink /tmp/plugtmp/home.mid immediately. That leaves timidity in a funk. See URL for exmaple. This is with Mozilla 1.0 RC2
Reporter | ||
Comment 2•22 years ago
|
||
I'm using plugger-4.0, here's my .mid section of /etc/plugger-4.0: audio/mid: midi,mid: MIDI audio file audio/x-mid: midi,mid: MIDI audio file audio/midi: midi,mid: MIDI audio file audio/x-midi: midi,mid: MIDI audio file : timidity -s 48000 -a -idqqqqqqq "$file" >/dev/null 2>/dev/null : playmidi $file Here's the section of the trace where you can see writes to FDs 38 (/tmp/plugtmp/home/.mid, 39 (cache), the plugger fork and the immediate unlink: 6621 gettimeofday({1021563233, 665779}, NULL) = 0 6621 write(38, "NW\201p\230QW\201p\230VW\201p\230UW\201p\210U@\0\210V@"..., 131 6621 write(39, "MThd\0\0\0\6\0\1\0\17\1\340MTrk\0\0\1{\0\377\1\24\221\305"..., 6621 close(38) = 0 6621 gettimeofday({1021563233, 669310}, NULL) = 0 6621 socketpair(PF_UNIX, SOCK_STREAM, 0, [38, 40]) = 0 6621 rt_sigprocmask(SIG_SETMASK, ~[], [RT_0], 8) = 0 6621 fork() = 6634 6621 kill(6627, SIGRT_0) = 0 6621 rt_sigprocmask(SIG_SETMASK, [RT_0], ~[KILL STOP], 8) = 0 6621 close(40) = 0 6621 stat("/tmp/plugtmp/home.mid", {st_mode=S_IFREG|0755, st_size=29550, ...}) 6621 lstat("/tmp/plugtmp/home.mid", {st_mode=S_IFREG|0755, st_size=29550, ...}) 6621 unlink("/tmp/plugtmp/home.mid") = 0 6621 gettimeofday({1021563233, 675254}, NULL) = 0 A bit later, plugger is called thusly: 6634 execve("/usr/bin/plugger-4.0", ["plugger-4.0", "0,2147483647,40,14680065,8,8,200"..., "/tmp/plugtmp/home.mid", "", ":0.0", "timidity -s 48000 -a -idqqqqqqq "..., "audio/midi"], [/* 55 vars */]) = 0 And then timidity tries to: 6636 open("/tmp/plugtmp/home.mid", O_RDONLY) = -1 ENOENT (No such file or directory)
Comment 3•22 years ago
|
||
assigning to serge
Assignee: beppe → serge
Priority: -- → P3
Target Milestone: --- → mozilla1.2beta
Assignee | ||
Comment 4•22 years ago
|
||
yes, it's true only for embed tag, and this is not linux only, changing OS, taking this bug.
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
OS: Linux → All
Updated•22 years ago
|
Whiteboard: [plugger] → [PL2:NA][plugger]
Target Milestone: mozilla1.2beta → mozilla1.0.2
Assignee | ||
Comment 5•22 years ago
|
||
this patch also implemented simple logic to reused files already copied in local plugin cache. Please review.
Comment 6•22 years ago
|
||
The patch seems to solve this problem, which was also affecting embedded audio files.
Comment on attachment 93770 [details] [diff] [review] patch v1 Couple of questions. - is my understanding correct that we are not going to delete this cache file at all under certain circumstances? - can we not use globals? How difficult is to add something like |nsPIPluginHost::GetActivePlugins|? Other than that r=av.
Attachment #93770 -
Flags: review+
Assignee | ||
Comment 8•22 years ago
|
||
>we are not going to delete this cache file at all under certain circumstances could you be more specific, please? all files should be deleted when last instance of plugin which uses those files are gone. >can we not use globals? why not? >How difficult is to add something like |nsPIPluginHost::GetActivePlugins|? difficulties are the same as for any new method to the interface. but my question is what benefits it will give us? I doubt we are going to use this method outside gkplugin.dll,. so, to me we'll gain nothing but extra code and additional QI call.
Updated•22 years ago
|
Target Milestone: mozilla1.0.2 → mozilla1.2alpha
>I doubt we are going to use this method outside gkplugin.dll,.
>so, to me we'll gain nothing but extra code and additional QI call.
Then it does not make sense to have it a member of |nsPluginHostImpl|. The code
will be even simpler. Should we consider this option?
Assignee | ||
Comment 10•22 years ago
|
||
>Should we consider this option?
as clean up effort - yes, but not for this bug i guess.
Assignee | ||
Comment 11•22 years ago
|
||
*** Bug 161224 has been marked as a duplicate of this bug. ***
Comment 13•22 years ago
|
||
The bug seems to be important. Please consider my r=av valid and seek for sr. Maybe this bug should get higher promotion.
Summary: plugger cannot read file because browser removes it too soon. → Plugins may starve when cached data they read from is removed too soon
Comment 14•22 years ago
|
||
Comment on attachment 93770 [details] [diff] [review] patch v1 >Index: nsPluginHostImpl.cpp how about adding a description of what this bit of code is trying to do? >+ nsActivePlugin *pActivePlugins = gActivePluginList->mFirst; >+ do { >+ // most recent streams are at the end of list >+ int cnt = pActivePlugins->mStreams->Count(); >+ for (int i = cnt; i; i-- ) { >+ nsPluginStreamListenerPeer * listenerPeer = >+ NS_REINTERPRET_CAST(nsPluginStreamListenerPeer *, pActivePlugins->mStreams->ElementAt(i-1)); >+ if (listenerPeer && >+ listenerPeer->mPluginStreamInfo->GetExistingPluginCacheFile(mPluginStreamInfo, getter_AddRefs(pluginTmp))) { >+ pActivePlugins = gActivePluginList->mLast; // break do{}while() loop >+ break; >+ } >+ } >+ } while (pActivePlugins = pActivePlugins->mNext); >+ >+ if (pluginTmp) { >+ mPluginStreamInfo->SetLocalCachedFile(pluginTmp); >+ } else { hmm... where did these URL strings come from? how do you know that case sensitive compare is ok? seems like you really should be using nsIURI::equals() to test this. >+ !PL_strcmp(psi->mURL, mURL))
Assignee | ||
Comment 15•22 years ago
|
||
thanks darin. >how about adding a description of what this bit of code is trying to do? will do. >hmm... where did these URL strings come from? it's declare as char* in class nsPluginStreamInfo, http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp&rev=1.408&root=/cvsroot#1275 why so, it's a different story. cannot do nsIURI::equals() on C string.
Comment 16•22 years ago
|
||
keep in mind that if you're comparing two URLs and not using nsIURI::equals, then you have a bug! probably fixing this bug requires significantly reworking the plugins to store nsIURI instead of |char*| for URLs.
Assignee | ||
Comment 17•22 years ago
|
||
yeap, there is a plan to rewrite plugins code, and we'll change char* url to nsIURI than, but for now it's char*.
Comment 18•22 years ago
|
||
Comment on attachment 93770 [details] [diff] [review] patch v1 sr=darin
Attachment #93770 -
Flags: superreview+
Assignee | ||
Comment 19•22 years ago
|
||
I apologize, the previous patch has wrong logic for removing locally cached file:( so, here's the new one. I've added new class |nsPluginLocalCachedFile : public nsISupports| which is responsible for removing the file in dtor. Please review.
Attachment #93770 -
Attachment is obsolete: true
Comment 20•22 years ago
|
||
Comment on attachment 95202 [details] [diff] [review] patch v2 You operate comptrs, why in this new nsPluginLocalCachedFile class we still pass raw pointers? I think something like this would be appropriate: + nsresult + SetFile(nsCOMPtr<nsIFile> aFile); + + nsresult + GetFile(nsCOMPtr<nsIFile> &aFile); |nsPluginStreamInfo::SetLocalCacheFile| can be adjusted too.
Assignee | ||
Comment 21•22 years ago
|
||
hmm, have you read? http://mozilla.org/projects/xpcom/nsCOMPtr.html#guide_nsCOMPtr_in_APIs e.g. void f( nsCOMPtr<T> ) don't pass an nsCOMPtr by value
Comment 22•22 years ago
|
||
Yep. & should be there obviously.
Comment 23•22 years ago
|
||
Comment on attachment 95202 [details] [diff] [review] patch v2 r=av
Attachment #95202 -
Flags: review+
Assignee | ||
Comment 24•22 years ago
|
||
Darin, would you please sr= on attachment 95202 [details] [diff] [review]? thanks.
Comment 25•22 years ago
|
||
*** Bug 163590 has been marked as a duplicate of this bug. ***
Comment 26•22 years ago
|
||
Just came to my mind. Not that I don't like the patch, but maybe this will make it simpler and smaller on footprint. As far as I understand the only reason to have |nsPluginLocalCachedFile| wrapper around |nsIFile| is that we want to delete the actual file when it is not longer needed, i.e. when the corresponding object's refcount goes to zero. Looks like we have a special macro NS_RELEASE2 which can be used to detect this situation. Do you think it would make sense to make use of it?
Assignee | ||
Comment 27•22 years ago
|
||
well, good point, let me see... probably using one extra refcnt for |mCachedFile| and do something like this in nsPluginStreamInfo::~nsPluginStreamInfo() { ... NS_RELEASE2(mCachedFile, refcnt); if (refcnt == 1) { mCachedFile->Remove(PR_FALSE); NS_RELEASE(mCachedFile); } we can do the job.
Assignee | ||
Comment 28•22 years ago
|
||
with NS_RELEASE2(mLocalCachedFile,rc) and nsActivePlugin.mStreams converted to nsISupportsArray instead of nsVoidArray
Attachment #95202 -
Attachment is obsolete: true
Comment 29•22 years ago
|
||
Comment on attachment 96129 [details] [diff] [review] patch v3 r=av, maybe to add some more explanation to a comment why we do this extra addref.
Attachment #96129 -
Flags: review+
Assignee | ||
Comment 30•22 years ago
|
||
#define NS_RELEASE2(_ptr,_rv) \ PR_BEGIN_MACRO \ _rv = NS_LOG_RELEASE_CALL((_ptr), (_ptr)->Release(),__FILE__,__LINE__); \ if (0 == (_rv)) (_ptr) = 0; \ PR_END_MACRO if _ptr == 0, I cannot do _ptr->Foo()
Comment 31•22 years ago
|
||
I am just suggesting to add more comments on this in the patch.
Assignee | ||
Comment 32•22 years ago
|
||
+ // add one extra refcnt, we can do NS_RELEASE2(mLocalCachedFile...) in dtor is this not enough? do I have to explain that _ptr->Foo() is bad if _ptr==0?
Comment 33•22 years ago
|
||
How about something like this: // we need this extra refcount in order to be able to remove the file physically in // the destructor, when it is released by everybody else
Assignee | ||
Comment 34•22 years ago
|
||
av: I'll add come comments, thanks. darin: would you please sr= on patch v3 (attachment 96129 [details] [diff] [review])? thanks.
Comment 35•22 years ago
|
||
Comment on attachment 96129 [details] [diff] [review] patch v3 >Index: nsPluginHostImpl.h > PRBool mStopped; > PRTime mllStopTime; > PRBool mDefaultPlugin; >+ PRBool mXPConnected; it might be worthwhile to convert these PRBool variables over to PRPackedBool, and list them adjacent to one another. that might reduce the size of the data structure a bit, fwiw. >Index: nsPluginHostImpl.cpp >+ nsresult rv = file->Clone(&mLocalCachedFile); >+ // add one extra refcnt, we can do NS_RELEASE2(mLocalCachedFile...) in dtor >+ NS_IF_ADDREF(mLocalCachedFile); so, just to make sure i understand what's going on here... setting mLocalCachedFile results in 2 references. the destructor will call release twice if there are only 2 references to the file, else it will only call release once. mLocalCachedFile could gain an additional reference if UseExistingPluginCacheFile is called, but that's ok since now you'd have two nsPluginStreamInfo objects owning 3 references to mLocalCachedFile. one of the psi's would end up calling release twice. is that correct? so, what happens if someone calls GetLocalCachedFile? aren't we in trouble if they don't do the same NS_RELEASE2 fu? can we possibly eliminate that method? does anyone call it?
Assignee | ||
Comment 36•22 years ago
|
||
>it might be worthwhile to convert these PRBool variables over to PRPackedBool I agree, but I did not introduce that var in this patch, and we'll change it in revise plugins code. >one of the psi's would end up calling release twice. is that correct? yes, this is correct, the final release of |mLocalCachedFile| will be call in |nsPluginStreamInfo::~nsPluginStreamInfo| ... + if (mLocalCachedFile) { + nsrefcnt refcnt; + NS_RELEASE2(mLocalCachedFile, refcnt); + if (refcnt == 1) { + mLocalCachedFile->Remove(PR_FALSE); + NS_RELEASE(mLocalCachedFile); + } only if (refcnt == 1), that is why I've added one extra refcnt in |nsPluginStreamInfo::SetLocalCachedFile| >so, what happens if someone calls GetLocalCachedFile? |GetLocalCachedFile| is *non* interface simple getter method void nsPluginStreamInfo::GetLocalCachedFile(nsIFile** file) { - *file = mCachedFile; + *file = mLocalCachedFile; NS_IF_ADDREF(*file); } >aren't we in trouble if they don't do the same NS_RELEASE2 fu? the caller has to call NS_RELEASE or NS_RELEASE2 w/o any problem. >can we possibly eliminate that method? why? what's risk to have this method in. >does anyone call it? we do call in once from |nsPluginStreamListenerPeer::OnStopRequest| to figure out which file to use in |OnFileAvailable|
Comment 37•22 years ago
|
||
so long as the reference, added when GetLocalCacheFile is called, is released before the destructor of any psi is invoked, you are ok. but if someone calls GetLocalCacheFile and holds onto that reference for any "long" period of time, you could end up leaking the mLocalCacheFile. that's my fear anyways. of course, the code as is doesn't leak, but someone down the road might call GetLocalCacheFile not knowing about this funky reference counting arrangement. can you do anything to prevent that kind of future problem?
Assignee | ||
Comment 38•22 years ago
|
||
changed method signature from void nsPluginStreamInfo::GetLocalCachedFile(nsIFile** file); to nsresult nsPluginStreamInfo::GetLocalCachedFile(nsCOMPtr<nsIFile>& file); of course it does not 100% bulletproof, but if someone will hold onto any reference more that it's needed we can leak any objects:(
Attachment #96129 -
Attachment is obsolete: true
Comment 39•22 years ago
|
||
not to be a pest, but i don't see how that change really helps... it seems like a large comment in the class definition would be sufficient :-/
Assignee | ||
Comment 40•22 years ago
|
||
Darin, I think to leak |mLocalCachedFile| using |nsPluginStreamInfo::GetLocalCachedFile(nsCOMPtr<nsIFile>& file)| is not a trivial task, |nsCOMPtr<nsIFile> file| will be released automatically when it goes out of scope. I cannot assume the scenario when it'll be possible to leak that.
Comment 41•22 years ago
|
||
{ nsCOMPtr<nsIFile> file; { nsCOMPtr<nsIPluginStreamInfo> psi = // create psi psi->GetLocalCachedFile(file); } } this admittedly simplified code leaks |file|. my point is that the leak can still happen easily. IMO, the v3 patch was fine... it just needed a comment explaining that callers should keep in mind the fact that should not hold a reference to the returned file for very long.
Assignee | ||
Comment 42•22 years ago
|
||
{ nsCOMPtr<nsIFile> file; { nsCOMPtr<nsIPluginStreamInfo> psi = // create psi psi->GetLocalCachedFile(file); } } <== |file| will be release out of this scope, how can we leak it? well, I can try to move |mLocalCachedFile| from |nsPluginStreamInfo| into |nsPluginStreamListenerPeer| and eliminate those |nsPluginStreamInfo::S/GetLocalCachedFile| methods, if you want me to do so.
Comment 43•22 years ago
|
||
like i said, i think it would be enough to add a BIG comment above the declaration of GetLocalCachedFile. that should be plenty to help out folks down the road, don't you think?
Assignee | ||
Comment 44•22 years ago
|
||
I agree, but now I'm really thinking to move |mLocalCachedFile| from |nsPluginStreamInfo| into |nsPluginStreamListenerPeer|. I see no reason to keep that member belong |nsPluginStreamInfo| if we access it only from |nsPluginStreamListenerPeer|. av, peterl what do you think?
Comment 45•22 years ago
|
||
Makes sense. What was the reason to have it in |nsPluginStreamInfo| in the first place?
Assignee | ||
Comment 46•22 years ago
|
||
that was introduced by fix for bug 87397
Assignee | ||
Comment 47•22 years ago
|
||
with |mLocalCachedFile| moved from |nsPluginStreamInfo| into |nsPluginStreamListenerPeer| i think it's more clear code.
Comment 48•22 years ago
|
||
Comment on attachment 96929 [details] [diff] [review] patch v5 This is nice clean up. Maybe it would make sense and use this opportunity to get rid |nsPluginHostImpl::mActivePlugins| list? We can just make it global rather than create a global reference to it.
Assignee | ||
Comment 49•22 years ago
|
||
of course we can, but I would not do that in this patch, the patch is big enough already, and changing |nsPluginHostImpl::mActivePluginList| could bring the risk which is not related to this bug at all.
Comment 50•22 years ago
|
||
Comment on attachment 96929 [details] [diff] [review] patch v5 r=av
Attachment #96929 -
Flags: review+
Comment 51•22 years ago
|
||
Comment on attachment 96929 [details] [diff] [review] patch v5 sr=darin
Attachment #96929 -
Flags: superreview+
Assignee | ||
Comment 52•22 years ago
|
||
on the trunk nsPluginHostImpl.cpp; new revision: 1.421; previous revision: 1.420 nsPluginHostImpl.h; new revision: 1.86; previous revision: 1.85 ... Thanks all
Status: ASSIGNED → RESOLVED
Closed: 22 years ago
Resolution: --- → FIXED
Updated•2 years ago
|
Product: Core → Core Graveyard
You need to log in
before you can comment on or make changes to this bug.
Description
•