Bug 1252152 - Make plugin instances destroyed while that instance is on the stack crash earlier and more usefully, r?jimm draft
authorBenjamin Smedberg <benjamin@smedbergs.us>
Wed, 06 Apr 2016 16:23:43 -0400
changeset 348952 1ab97ec1674f3e39554b5c277df1eb50120883a6
parent 347760 17a0ded9bb99c05c25729c306b91771483109067
child 517985 d0d056d75a54bec13bdd9bd6dd397bdfed1b3679
push id14972
push userbsmedberg@mozilla.com
push dateFri, 08 Apr 2016 16:37:22 +0000
reviewersjimm
bugs1252152
milestone48.0a1
Bug 1252152 - Make plugin instances destroyed while that instance is on the stack crash earlier and more usefully, r?jimm MozReview-Commit-ID: EblMTq3CGn9
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginScriptableObjectChild.cpp
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -191,16 +191,17 @@ PluginInstanceChild::PluginInstanceChild
     , mSurfaceType(gfxSurfaceType::Max)
     , mCurrentInvalidateTask(nullptr)
     , mCurrentAsyncSetWindowTask(nullptr)
     , mPendingPluginCall(false)
     , mDoAlphaExtraction(false)
     , mHasPainted(false)
     , mSurfaceDifferenceRect(0,0,0,0)
     , mDestroyed(false)
+    , mStackDepth(0)
 {
     memset(&mWindow, 0, sizeof(mWindow));
     mWindow.type = NPWindowTypeWindow;
     mData.ndata = (void*) this;
     mData.pdata = nullptr;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     mWindow.ws_info = &mWsInfo;
     memset(&mWsInfo, 0, sizeof(mWsInfo));
@@ -378,16 +379,17 @@ PluginInstanceChild::InternalGetNPObject
 }
 
 NPError
 PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
                                   void* aValue)
 {
     PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar));
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
     switch(aVar) {
 
 #if defined(MOZ_X11)
     case NPNVToolkit:
         *((NPNToolkitType*)aValue) = NPNVGtk2;
         return NPERR_NO_ERROR;
 
@@ -586,16 +588,18 @@ PluginInstanceChild::Invalidate()
 NPError
 PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
 {
     MOZ_LOG(GetPluginLog(), LogLevel::Debug, ("%s (aVar=%i, aValue=%p)",
                                       FULLFUNCTION, (int) aVar, aValue));
 
     AssertPluginThread();
 
+    AutoStackHelper guard(this);
+
     switch (aVar) {
     case NPPVpluginWindowBool: {
         NPError rv;
         bool windowed = (NPBool) (intptr_t) aValue;
 
         if (!CallNPN_SetValue_NPPVpluginWindow(windowed, &rv))
             return NPERR_GENERIC_ERROR;
 
@@ -690,16 +694,17 @@ PluginInstanceChild::NPN_SetValue(NPPVar
     }
 }
 
 bool
 PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(
     bool* wantsAllStreams, NPError* rv)
 {
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
     uint32_t value = 0;
     if (!mPluginIface->getvalue) {
         *rv = NPERR_GENERIC_ERROR;
     }
     else {
         *rv = mPluginIface->getvalue(GetNPP(), NPPVpluginWantsAllNetworkStreams,
                                      &value);
@@ -708,16 +713,17 @@ PluginInstanceChild::AnswerNPP_GetValue_
     return true;
 }
 
 bool
 PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(
     bool* needs, NPError* rv)
 {
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
 #ifdef MOZ_X11
     // The documentation on the types for many variables in NP(N|P)_GetValue
     // is vague.  Often boolean values are NPBool (1 byte), but
     // https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins
     // treats NPPVpluginNeedsXEmbed as PRBool (int), and
     // on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|.
     // thus we can't use NPBool for needsXEmbed, or the three bytes above
@@ -742,16 +748,17 @@ PluginInstanceChild::AnswerNPP_GetValue_
 }
 
 bool
 PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginScriptableNPObject(
                                           PPluginScriptableObjectChild** aValue,
                                           NPError* aResult)
 {
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
     NPObject* object = nullptr;
     NPError result = NPERR_GENERIC_ERROR;
     if (mPluginIface->getvalue) {
         result = mPluginIface->getvalue(GetNPP(), NPPVpluginScriptableNPObject,
                                         &object);
     }
     if (result == NPERR_NO_ERROR && object) {
@@ -779,16 +786,17 @@ PluginInstanceChild::AnswerNPP_GetValue_
 }
 
 bool
 PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(
                                           nsCString* aPlugId,
                                           NPError* aResult)
 {
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
 #if MOZ_ACCESSIBILITY_ATK
 
     char* plugId = nullptr;
     NPError result = NPERR_GENERIC_ERROR;
     if (mPluginIface->getvalue) {
         result = mPluginIface->getvalue(GetNPP(),
                                         NPPVpluginNativeAccessibleAtkPlugId,
@@ -851,16 +859,17 @@ PluginInstanceChild::AnswerNPP_SetValue_
 }
 
 bool
 PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
                                            int16_t* handled)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
 #if defined(MOZ_X11) && defined(DEBUG)
     if (GraphicsExpose == event.event.type)
         PLUGIN_LOG_DEBUG(("  received drawable 0x%lx\n",
                           event.event.xgraphicsexpose.drawable));
 #endif
 
 #ifdef XP_MACOSX
@@ -933,16 +942,17 @@ PluginInstanceChild::AnswerNPP_HandleEve
 bool
 PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
                                                  Shmem&& mem,
                                                  int16_t* handled,
                                                  Shmem* rtnmem)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
     PaintTracker pt;
 
     NPCocoaEvent evcopy = event.event;
     mContentsScaleFactor = event.contentsScaleFactor;
 
     if (evcopy.type == NPCocoaEventDrawRect) {
         int scaleFactor = ceil(mContentsScaleFactor);
@@ -1037,16 +1047,17 @@ PluginInstanceChild::CGDraw(CGContextRef
 
 bool
 PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
                                                      const uint32_t &surfaceid,
                                                      int16_t* handled)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
     PaintTracker pt;
 
     NPCocoaEvent evcopy = event.event;
     mContentsScaleFactor = event.contentsScaleFactor;
     RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(surfaceid,
                                                             mContentsScaleFactor);
     if (!surf) {
@@ -1232,16 +1243,17 @@ PluginInstanceChild::AnswerNPP_SetWindow
     PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
                       FULLFUNCTION,
                       aWindow.window,
                       aWindow.x, aWindow.y,
                       aWindow.width, aWindow.height));
     NS_ASSERTION(!mLayersRendering && !mPendingPluginCall,
                  "Shouldn't be receiving NPP_SetWindow with layer rendering");
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     NS_ASSERTION(mWsInfo.display, "We should have a valid display!");
 
     // The minimum info is sent over IPC to allow this
     // code to determine the rest.
 
     mWindow.x = aWindow.x;
@@ -2485,16 +2497,17 @@ PluginInstanceChild::RecvPBrowserStreamC
 
 NPError
 PluginInstanceChild::DoNPP_NewStream(BrowserStreamChild* actor,
                                      const nsCString& mimeType,
                                      const bool& seekable,
                                      uint16_t* stype)
 {
     AssertPluginThread();
+    AutoStackHelper guard(this);
     NPError rv = actor->StreamConstructed(mimeType, seekable, stype);
     return rv;
 }
 
 bool
 PluginInstanceChild::AnswerNPP_NewStream(PBrowserStreamChild* actor,
                                          const nsCString& mimeType,
                                          const bool& seekable,
@@ -2703,16 +2716,17 @@ PluginInstanceChild::GetActorForNPObject
     return actor;
 }
 
 NPError
 PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
                                    NPStream** aStream)
 {
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
     PluginStreamChild* ps = new PluginStreamChild();
 
     NPError result;
     CallPPluginStreamConstructor(ps, nsDependentCString(aMIMEType),
                                  NullableString(aWindow), &result);
     if (NPERR_NO_ERROR != result) {
         *aStream = nullptr;
@@ -2785,16 +2799,17 @@ NPRectToIntRect(const NPRect& in)
     return IntRect(in.left, in.top, in.right - in.left, in.bottom - in.top);
 }
 
 NPError
 PluginInstanceChild::NPN_InitAsyncSurface(NPSize *size, NPImageFormat format,
                                           void *initData, NPAsyncSurface *surface)
 {
     AssertPluginThread();
+    AutoStackHelper guard(this);
 
     if (!IsUsingDirectDrawing()) {
         return NPERR_INVALID_PARAM;
     }
     if (format != NPImageFormatBGRA32 && format != NPImageFormatBGRX32) {
         return NPERR_INVALID_PARAM;
     }
 
@@ -3002,16 +3017,17 @@ PluginInstanceChild::DoAsyncRedraw()
 }
 
 bool
 PluginInstanceChild::RecvAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
                                         const NPRemoteWindow& aWindow)
 {
     AssertPluginThread();
 
+    AutoStackHelper guard(this);
     NS_ASSERTION(!aWindow.window, "Remote window should be null.");
 
     if (mCurrentAsyncSetWindowTask) {
         mCurrentAsyncSetWindowTask->Cancel();
         mCurrentAsyncSetWindowTask = nullptr;
     }
 
     // We shouldn't process this now because it may be received within a nested
@@ -4256,16 +4272,19 @@ DeleteObjects(nsTHashtable<DeletingObjec
 }
 
 void
 PluginInstanceChild::Destroy()
 {
     if (mDestroyed) {
         return;
     }
+    if (mStackDepth != 0) {
+        NS_RUNTIMEABORT("Destroying plugin instance on the stack.");
+    }
     mDestroyed = true;
 
 #if defined(OS_WIN)
     SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
 #endif
 
     InfallibleTArray<PBrowserStreamChild*> streams;
     ManagedPBrowserStreamChild(streams);
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -655,14 +655,33 @@ private:
 
     // Cached rectangle rendered to previous surface(mBackSurface)
     // Used for reading back to current surface and syncing data,
     // in plugin coordinates.
     nsIntRect mSurfaceDifferenceRect;
 
     // Has this instance been destroyed, either by ActorDestroy or NPP_Destroy?
     bool mDestroyed;
+
+    // A counter is incremented by AutoStackHelper to indicate that there is an
+    // active plugin call which should be preventing shutdown.
+public:
+    class AutoStackHelper {
+    public:
+        AutoStackHelper(PluginInstanceChild* instance)
+            : mInstance(instance)
+        {
+            ++mInstance->mStackDepth;
+        }
+        ~AutoStackHelper() {
+            --mInstance->mStackDepth;
+        }
+    private:
+        PluginInstanceChild *const mInstance;
+    };
+private:
+    int32_t mStackDepth;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // ifndef dom_plugins_PluginInstanceChild_h
--- a/dom/plugins/ipc/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -333,16 +333,18 @@ PluginScriptableObjectChild::ScriptableG
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
+  PluginInstanceChild::AutoStackHelper guard(actor->mInstance);
+
   Variant result;
   bool success;
   actor->CallGetParentProperty(FromNPIdentifier(aName),
                                &result, &success);
 
   if (!success) {
     return false;
   }
@@ -702,16 +704,17 @@ PluginScriptableObjectChild::NPObjectDes
   mInvalidated = true;
   mObject = nullptr;
 }
 
 bool
 PluginScriptableObjectChild::AnswerInvalidate()
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     return true;
   }
 
   mInvalidated = true;
 
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
@@ -726,16 +729,17 @@ PluginScriptableObjectChild::AnswerInval
   return true;
 }
 
 bool
 PluginScriptableObjectChild::AnswerHasMethod(const PluginIdentifier& aId,
                                              bool* aHasMethod)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerHasMethod with an invalidated object!");
     *aHasMethod = false;
     return true;
   }
 
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
@@ -753,16 +757,17 @@ PluginScriptableObjectChild::AnswerHasMe
 
 bool
 PluginScriptableObjectChild::AnswerInvoke(const PluginIdentifier& aId,
                                           InfallibleTArray<Variant>&& aArgs,
                                           Variant* aResult,
                                           bool* aSuccess)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerInvoke with an invalidated object!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
@@ -823,16 +828,17 @@ PluginScriptableObjectChild::AnswerInvok
 }
 
 bool
 PluginScriptableObjectChild::AnswerInvokeDefault(InfallibleTArray<Variant>&& aArgs,
                                                  Variant* aResult,
                                                  bool* aSuccess)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerInvokeDefault with an invalidated object!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
@@ -891,16 +897,17 @@ PluginScriptableObjectChild::AnswerInvok
   return true;
 }
 
 bool
 PluginScriptableObjectChild::AnswerHasProperty(const PluginIdentifier& aId,
                                                bool* aHasProperty)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerHasProperty with an invalidated object!");
     *aHasProperty = false;
     return true;
   }
 
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
@@ -919,16 +926,17 @@ PluginScriptableObjectChild::AnswerHasPr
 bool
 PluginScriptableObjectChild::AnswerGetChildProperty(const PluginIdentifier& aId,
                                                     bool* aHasProperty,
                                                     bool* aHasMethod,
                                                     Variant* aResult,
                                                     bool* aSuccess)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   *aHasProperty = *aHasMethod = *aSuccess = false;
   *aResult = void_t();
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerGetProperty with an invalidated object!");
     return true;
   }
@@ -967,16 +975,17 @@ PluginScriptableObjectChild::AnswerGetCh
 }
 
 bool
 PluginScriptableObjectChild::AnswerSetProperty(const PluginIdentifier& aId,
                                                const Variant& aValue,
                                                bool* aSuccess)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerSetProperty with an invalidated object!");
     *aSuccess = false;
     return true;
   }
 
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
@@ -1005,16 +1014,17 @@ PluginScriptableObjectChild::AnswerSetPr
   return true;
 }
 
 bool
 PluginScriptableObjectChild::AnswerRemoveProperty(const PluginIdentifier& aId,
                                                   bool* aSuccess)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!");
     *aSuccess = false;
     return true;
   }
 
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
@@ -1035,16 +1045,17 @@ PluginScriptableObjectChild::AnswerRemov
   return true;
 }
 
 bool
 PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
                                              bool* aSuccess)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
     *aSuccess = false;
     return true;
   }
 
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
@@ -1074,16 +1085,17 @@ PluginScriptableObjectChild::AnswerEnume
 }
 
 bool
 PluginScriptableObjectChild::AnswerConstruct(InfallibleTArray<Variant>&& aArgs,
                                              Variant* aResult,
                                              bool* aSuccess)
 {
   AssertPluginThread();
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerConstruct with an invalidated object!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
@@ -1160,16 +1172,18 @@ PluginScriptableObjectChild::RecvUnprote
   Unprotect();
   return true;
 }
 
 bool
 PluginScriptableObjectChild::Evaluate(NPString* aScript,
                                       NPVariant* aResult)
 {
+  PluginInstanceChild::AutoStackHelper guard(mInstance);
+
   nsDependentCString script("");
   if (aScript->UTF8Characters && aScript->UTF8Length) {
     script.Rebind(aScript->UTF8Characters, aScript->UTF8Length);
   }
 
   bool success;
   Variant result;
   CallNPN_Evaluate(script, &result, &success);