Bug 829557. Part 1: When calling into plugin code, identify situations where it is safe (or unsafe) to reenter Gecko from plugin code. r=bsmedberg

When Gecko is reentered from unsafe call sites, we allow the reentry, but we
suppress execution of the refresh driver to minimize the danger.
In this patch, we treat all sites as unsafe.

--HG--
rename : toolkit/modules/Timer.jsm => browser/devtools/shared/Browser.jsm
rename : toolkit/modules/tests/xpcshell/test_timer.js => browser/devtools/shared/test/browser_browser_basic.js
rename : build/mach_bootstrap.py => mach
extra : rebase_source : b83c1d09313bff62357eaa931eced0f72f838493
This commit is contained in:
Robert O'Callahan 2013-02-28 00:50:27 +13:00
parent fffa15107b
commit e170f559fb
10 changed files with 82 additions and 38 deletions

View File

@ -41,6 +41,7 @@ EXPORTS = \
nsPluginsCID.h \
nsNPAPIPluginInstance.h \
nsPluginsDir.h \
nsPluginSafety.h \
nsPluginTags.h \
nsPluginDirServiceProvider.h \
nsPluginHost.h \

View File

@ -186,9 +186,9 @@ enum eNPPStreamTypeInternal {
static NS_DEFINE_IID(kMemoryCID, NS_MEMORY_CID);
PRIntervalTime NS_NotifyBeginPluginCall()
PRIntervalTime NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState)
{
nsNPAPIPluginInstance::BeginPluginCall();
nsNPAPIPluginInstance::BeginPluginCall(aReentryState);
return PR_IntervalNow();
}
@ -196,9 +196,9 @@ PRIntervalTime NS_NotifyBeginPluginCall()
// registered to listen to the "experimental-notify-plugin-call" subject.
// Each "experimental-notify-plugin-call" notification carries with it the run
// time value in milliseconds that the call took to execute.
void NS_NotifyPluginCall(PRIntervalTime startTime)
void NS_NotifyPluginCall(PRIntervalTime startTime, NSPluginCallReentry aReentryState)
{
nsNPAPIPluginInstance::EndPluginCall();
nsNPAPIPluginInstance::EndPluginCall(aReentryState);
PRIntervalTime endTime = PR_IntervalNow() - startTime;
nsCOMPtr<nsIObserverService> notifyUIService =
@ -789,7 +789,8 @@ nsPluginThreadRunnable::Run()
if (mFunc) {
PluginDestructionGuard guard(mInstance);
NS_TRY_SAFE_CALL_VOID(mFunc(mUserData), nullptr);
NS_TRY_SAFE_CALL_VOID(mFunc(mUserData), nullptr,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
}
return NS_OK;

View File

@ -213,7 +213,7 @@ nsNPAPIPluginInstance::~nsNPAPIPluginInstance()
}
}
uint32_t nsNPAPIPluginInstance::gInPluginCalls = 0;
uint32_t nsNPAPIPluginInstance::gInUnsafePluginCalls = 0;
void
nsNPAPIPluginInstance::Destroy()
@ -317,7 +317,8 @@ nsresult nsNPAPIPluginInstance::Stop()
if (pluginFunctions->destroy) {
NPSavedData *sdata = 0;
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroy)(&mNPP, &sdata), this);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroy)(&mNPP, &sdata), this,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("NPP Destroy called: this=%p, npp=%p, return=%d\n", this, &mNPP, error));
@ -582,7 +583,8 @@ nsresult nsNPAPIPluginInstance::SetWindow(NPWindow* window)
NPPAutoPusher nppPusher(&mNPP);
DebugOnly<NPError> error;
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setwindow)(&mNPP, (NPWindow*)window), this);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setwindow)(&mNPP, (NPWindow*)window), this,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
mInPluginInitCall = oldVal;
@ -650,7 +652,8 @@ nsresult nsNPAPIPluginInstance::Print(NPPrint* platformPrint)
}
if (pluginFunctions->print)
NS_TRY_SAFE_CALL_VOID((*pluginFunctions->print)(&mNPP, thePrint), this);
NS_TRY_SAFE_CALL_VOID((*pluginFunctions->print)(&mNPP, thePrint), this,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("NPP PrintProc called: this=%p, pDC=%p, [x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d]\n",
@ -688,7 +691,8 @@ nsresult nsNPAPIPluginInstance::HandleEvent(void* event, int16_t* result)
if (pluginFunctions->event) {
mCurrentPluginEvent = event;
#if defined(XP_WIN) || defined(XP_OS2)
NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), this);
NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), this,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
#else
MAIN_THREAD_JNI_REF_GUARD;
tmpResult = (*pluginFunctions->event)(&mNPP, event);
@ -718,7 +722,8 @@ nsresult nsNPAPIPluginInstance::GetValueFromPlugin(NPPVariable variable, void* v
PluginDestructionGuard guard(this);
NPError pluginError = NPERR_GENERIC_ERROR;
NS_TRY_SAFE_CALL_RETURN(pluginError, (*pluginFunctions->getvalue)(&mNPP, variable, value), this);
NS_TRY_SAFE_CALL_RETURN(pluginError, (*pluginFunctions->getvalue)(&mNPP, variable, value), this,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("NPP GetValue called: this=%p, npp=%p, var=%d, value=%d, return=%d\n",
this, &mNPP, variable, value, pluginError));
@ -1404,7 +1409,8 @@ nsNPAPIPluginInstance::PrivateModeStateChanged(bool enabled)
NPError error;
NPBool value = static_cast<NPBool>(enabled);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value), this);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value), this,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
}

View File

@ -50,6 +50,18 @@ const NPDrawingModel kDefaultDrawingModel = NPDrawingModelCoreGraphics;
const NPDrawingModel kDefaultDrawingModel = static_cast<NPDrawingModel>(0);
#endif
/**
* Used to indicate whether it's OK to reenter Gecko and repaint, flush frames,
* run scripts, etc, during this plugin call.
* When NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO is set, we try to avoid dangerous
* Gecko activities when the plugin spins a nested event loop, on a best-effort
* basis.
*/
enum NSPluginCallReentry {
NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO
};
class nsNPAPITimer
{
public:
@ -273,12 +285,19 @@ public:
// Returns the contents scale factor of the screen the plugin is drawn on.
double GetContentsScaleFactor();
static bool InPluginCall() { return gInPluginCalls > 0; }
static void BeginPluginCall() { ++gInPluginCalls; }
static void EndPluginCall()
static bool InPluginCallUnsafeForReentry() { return gInUnsafePluginCalls > 0; }
static void BeginPluginCall(NSPluginCallReentry aReentryState)
{
NS_ASSERTION(InPluginCall(), "Must be in plugin call");
--gInPluginCalls;
if (aReentryState == NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO) {
++gInUnsafePluginCalls;
}
}
static void EndPluginCall(NSPluginCallReentry aReentryState)
{
if (aReentryState == NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO) {
NS_ASSERTION(gInUnsafePluginCalls > 0, "Must be in plugin call");
--gInUnsafePluginCalls;
}
}
protected:
@ -376,7 +395,7 @@ private:
// is this instance Java and affected by bug 750480?
bool mHaveJavaC2PJSObjectQuirk;
static uint32_t gInPluginCalls;
static uint32_t gInUnsafePluginCalls;
};
#endif // nsNPAPIPluginInstance_h_

View File

@ -235,7 +235,8 @@ nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason)
NPPAutoPusher nppPusher(npp);
NPError error;
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n",
@ -273,7 +274,8 @@ nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason)
NPP npp;
mInst->GetNPP(&npp);
NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst);
NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n",
@ -321,7 +323,8 @@ nsNPAPIPluginStreamListener::OnStartBinding(nsPluginStreamListenerPeer* streamPe
NPPAutoPusher nppPusher(npp);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n",
@ -563,7 +566,8 @@ nsNPAPIPluginStreamListener::OnDataAvailable(nsPluginStreamListenerPeer* streamP
if (pluginFunctions->writeready) {
NPPAutoPusher nppPusher(npp);
NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst);
NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
("NPP WriteReady called: this=%p, npp=%p, "
"return(towrite)=%d, url=%s\n",
@ -612,7 +616,8 @@ nsNPAPIPluginStreamListener::OnDataAvailable(nsPluginStreamListenerPeer* streamP
NPPAutoPusher nppPusher(npp);
int32_t writeCount = 0; // bytes consumed by plugin instance
NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst);
NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, "
@ -720,7 +725,8 @@ nsNPAPIPluginStreamListener::OnFileAvailable(nsPluginStreamListenerPeer* streamP
NPP npp;
mInst->GetNPP(&npp);
NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst);
NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n",
@ -862,7 +868,8 @@ nsNPAPIPluginStreamListener::HandleRedirectNotification(nsIChannel *oldChannel,
NPP npp;
mInst->GetNPP(&npp);
#if defined(XP_WIN) || defined(XP_OS2)
NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData), mInst);
NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData), mInst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
#else
MAIN_THREAD_JNI_REF_GUARD;
(*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData);

View File

@ -173,7 +173,8 @@ static bool ProcessFlashMessageDelayed(nsPluginNativeWindowOS2 * aWin,
if (msg == sWM_FLASHBOUNCEMSG) {
// See PluginWindowEvent::Run() below.
NS_TRY_SAFE_CALL_VOID((aWin->GetWindowProc())(hWnd, WM_USER_FLASH, mp1, mp2),
inst);
inst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
return TRUE;
}
@ -309,9 +310,11 @@ static MRESULT EXPENTRY PluginWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM m
MRESULT res = (MRESULT)TRUE;
if (win->mPluginType == nsPluginType_Java_vm)
NS_TRY_SAFE_CALL_RETURN(res, ::WinDefWindowProc(hWnd, msg, mp1, mp2), inst);
NS_TRY_SAFE_CALL_RETURN(res, ::WinDefWindowProc(hWnd, msg, mp1, mp2), inst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
else
NS_TRY_SAFE_CALL_RETURN(res, (win->GetWindowProc())(hWnd, msg, mp1, mp2), inst);
NS_TRY_SAFE_CALL_RETURN(res, (win->GetWindowProc())(hWnd, msg, mp1, mp2), inst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
if (inst) {
// Popups are enabled (were enabled before the call to
@ -397,7 +400,8 @@ NS_IMETHODIMP PluginWindowEvent::Run()
// is more generic.
NS_TRY_SAFE_CALL_VOID((win->GetWindowProc())
(hWnd, GetMsg(), GetWParam(), GetLParam()),
inst);
inst,
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
Clear();
return NS_OK;

View File

@ -8,6 +8,8 @@
#include <prinrval.h>
enum NSPluginCallReentry;
// On Android, we need to guard against plugin code leaking entries in the local
// JNI ref table. See https://bugzilla.mozilla.org/show_bug.cgi?id=780831#c21
#ifdef MOZ_WIDGET_ANDROID
@ -18,23 +20,23 @@
#define MAIN_THREAD_JNI_REF_GUARD
#endif
PRIntervalTime NS_NotifyBeginPluginCall();
void NS_NotifyPluginCall(PRIntervalTime);
PRIntervalTime NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState);
void NS_NotifyPluginCall(PRIntervalTime aTime, NSPluginCallReentry aReentryState);
#define NS_TRY_SAFE_CALL_RETURN(ret, fun, pluginInst) \
#define NS_TRY_SAFE_CALL_RETURN(ret, fun, pluginInst, pluginCallReentry) \
PR_BEGIN_MACRO \
MAIN_THREAD_JNI_REF_GUARD; \
PRIntervalTime startTime = NS_NotifyBeginPluginCall(); \
PRIntervalTime startTime = NS_NotifyBeginPluginCall(pluginCallReentry); \
ret = fun; \
NS_NotifyPluginCall(startTime); \
NS_NotifyPluginCall(startTime, pluginCallReentry); \
PR_END_MACRO
#define NS_TRY_SAFE_CALL_VOID(fun, pluginInst) \
#define NS_TRY_SAFE_CALL_VOID(fun, pluginInst, pluginCallReentry) \
PR_BEGIN_MACRO \
MAIN_THREAD_JNI_REF_GUARD; \
PRIntervalTime startTime = NS_NotifyBeginPluginCall(); \
PRIntervalTime startTime = NS_NotifyBeginPluginCall(pluginCallReentry); \
fun; \
NS_NotifyPluginCall(startTime); \
NS_NotifyPluginCall(startTime, pluginCallReentry); \
PR_END_MACRO
#endif //nsPluginSafety_h_

View File

@ -820,7 +820,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
"Shouldn't have a JSContext on the stack");
if (nsNPAPIPluginInstance::InPluginCall()) {
if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
NS_ERROR("Refresh driver should not run during plugin call!");
// Try to survive this by just ignoring the refresh tick.
return;

View File

@ -2071,6 +2071,8 @@ nsObjectFrame::HandleEvent(nsPresContext* aPresContext,
if (mInstanceOwner->SendNativeEvents() &&
NS_IS_PLUGIN_EVENT(anEvent)) {
*anEventStatus = mInstanceOwner->ProcessEvent(*anEvent);
// Due to plugin code reentering Gecko, this frame may be dead at this
// point.
return rv;
}
@ -2085,6 +2087,8 @@ nsObjectFrame::HandleEvent(nsPresContext* aPresContext,
anEvent->message == NS_WHEEL_WHEEL) &&
mInstanceOwner->GetEventModel() == NPEventModelCocoa) {
*anEventStatus = mInstanceOwner->ProcessEvent(*anEvent);
// Due to plugin code reentering Gecko, this frame may be dead at this
// point.
return rv;
}
#endif

0
mach Executable file → Normal file
View File