Bug 544345: Kill plugins if they exceed the hang timeout. r=bsmedberg

--HG--
extra : transplant_source : K%BBb%8Dq%C8%26O%7Cj%10%0B%EE%05j%3C%0F%CA%C3%3A
This commit is contained in:
Chris Jones 2010-02-11 14:32:18 -06:00
Родитель 3a405d5c52
Коммит 1e29088232
3 изменённых файлов: 99 добавлений и 7 удалений

Просмотреть файл

@ -36,20 +36,36 @@
*
* ***** END LICENSE BLOCK ***** */
#include "base/process_util.h"
#include "mozilla/ipc/SyncChannel.h"
#include "mozilla/plugins/PluginModuleParent.h"
#include "mozilla/plugins/BrowserStreamParent.h"
#include "nsContentUtils.h"
#include "nsCRT.h"
#include "nsNPAPIPlugin.h"
using mozilla::PluginLibrary;
using base::KillProcess;
using mozilla::PluginLibrary;
using mozilla::ipc::NPRemoteIdentifier;
using mozilla::ipc::SyncChannel;
using namespace mozilla::plugins;
static const char kTimeoutPref[] = "dom.ipc.plugins.timeoutSecs";
PR_STATIC_ASSERT(sizeof(NPIdentifier) == sizeof(void*));
template<>
struct RunnableMethodTraits<mozilla::plugins::PluginModuleParent>
{
typedef mozilla::plugins::PluginModuleParent Class;
static void RetainCallee(Class* obj) { }
static void ReleaseCallee(Class* obj) { }
};
class PluginCrashed : public nsRunnable
{
public:
@ -80,6 +96,8 @@ PluginModuleParent::LoadModule(const char* aFilePath)
parent->Open(parent->mSubprocess->GetChannel(),
parent->mSubprocess->GetChildProcessHandle());
TimeoutChanged(kTimeoutPref, parent);
return parent;
}
@ -96,6 +114,8 @@ PluginModuleParent::PluginModuleParent(const char* aFilePath)
if (!mValidIdentifiers.Init()) {
NS_ERROR("Out of memory");
}
nsContentUtils::RegisterPrefCallback(kTimeoutPref, TimeoutChanged, this);
}
PluginModuleParent::~PluginModuleParent()
@ -111,6 +131,8 @@ PluginModuleParent::~PluginModuleParent()
mSubprocess->Delete();
mSubprocess = nsnull;
}
nsContentUtils::UnregisterPrefCallback(kTimeoutPref, TimeoutChanged, this);
}
void
@ -172,6 +194,58 @@ PluginModuleParent::WriteExtraDataForMinidump(nsIFile* dumpFile)
stream->Close();
}
int
PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
{
AssertPluginThread();
NS_ABORT_IF_FALSE(!strcmp(aPref, kTimeoutPref),
"unexpected pref callback");
PRInt32 timeoutSecs = nsContentUtils::GetIntPref(kTimeoutPref, 0);
int32 timeoutMs = (timeoutSecs > 0) ? (1000 * timeoutSecs) :
SyncChannel::kNoTimeout;
static_cast<PluginModuleParent*>(aModule)->SetReplyTimeoutMs(timeoutMs);
return 0;
}
void
PluginModuleParent::CleanupFromTimeout()
{
if (!mShutdown)
Close();
}
bool
PluginModuleParent::ShouldContinueFromReplyTimeout()
{
// FIXME/bug 544095: pop up a dialog asking the user what to do
bool waitMoar = false;
if (!waitMoar) {
// We can't depend on the IO thread notifying us of a channel
// error, because there's an inherent race between killing the
// subprocess and shutting down the socket. It would be nice
// to call Close() here and do all the IPDL cleanup
// immediately, but we might have arbitrary junk below us on
// the stack. So, a compromise: enqueue an event now that
// will Close(), *before* killing the child process. This
// guarantees that the Close() event will be processed before
// the IO error event, if it's delivered.
MessageLoop::current()->PostTask(
FROM_HERE,
NewRunnableMethod(this, &PluginModuleParent::CleanupFromTimeout));
// FIXME/bug 544095: kill the subprocess in a way that
// triggers breakpad, and also capture a minidump for this
// process
KillProcess(ChildProcessHandle(), 1, false);
}
return waitMoar;
}
void
PluginModuleParent::ActorDestroy(ActorDestroyReason why)
{

Просмотреть файл

@ -112,6 +112,16 @@ public:
*/
static PluginLibrary* LoadModule(const char* aFilePath);
const NPNetscapeFuncs* GetNetscapeFuncs() {
return mNPNIface;
}
bool EnsureValidNPIdentifier(NPIdentifier aIdentifier);
protected:
NS_OVERRIDE
virtual bool ShouldContinueFromReplyTimeout();
virtual bool
AnswerNPN_UserAgent(nsCString* userAgent);
@ -142,15 +152,9 @@ public:
NPError* aError,
bool* aBoolVal);
const NPNetscapeFuncs* GetNetscapeFuncs() {
return mNPNIface;
}
static PluginInstanceParent* InstCast(NPP instance);
static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
bool EnsureValidNPIdentifier(NPIdentifier aIdentifier);
base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
private:
void SetPluginFuncs(NPPluginFuncs* aFuncs);
@ -215,6 +219,9 @@ private:
void WriteExtraDataEntry(nsIFileOutputStream* stream,
const char* key,
const char* value);
void CleanupFromTimeout();
static int TimeoutChanged(const char* aPref, void* aModule);
PluginProcessParent* mSubprocess;
bool mShutdown;
const NPNetscapeFuncs* mNPNIface;

Просмотреть файл

@ -1215,6 +1215,17 @@ pref("editor.positioning.offset", 0);
pref("dom.max_chrome_script_run_time", 20);
pref("dom.max_script_run_time", 10);
// How long a plugin is allowed to process a synchronous IPC message
// before we consider it "hung".
//
// NB: chosen to match dom.max_script_run_time by default
#ifndef DEBUG
pref("dom.ipc.plugins.timeoutSecs", 10);
#else
// No timeout in DEBUG builds
pref("dom.ipc.plugins.timeoutSecs", 0);
#endif
pref("svg.enabled", true);
pref("svg.smil.enabled", true);