LCOV - code coverage report
Current view: top level - security/manager/ssl - nsNSSShutDown.h (source / functions) Hit Total Coverage
Test: output.info Lines: 15 31 48.4 %
Date: 2017-07-14 16:53:18 Functions: 3 9 33.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #ifndef nsNSSShutDown_h
       6             : #define nsNSSShutDown_h
       7             : 
       8             : #include "PLDHashTable.h"
       9             : #include "mozilla/Assertions.h"
      10             : #include "mozilla/CondVar.h"
      11             : #include "mozilla/Mutex.h"
      12             : #include "mozilla/StaticMutex.h"
      13             : #include "nscore.h"
      14             : #include "nspr.h"
      15             : 
      16             : class nsNSSShutDownObject;
      17             : class nsOnPK11LogoutCancelObject;
      18             : 
      19             : // Singleton, owned by nsNSSShutDownList
      20             : class nsNSSActivityState
      21             : {
      22             : public:
      23             :   nsNSSActivityState();
      24             :   ~nsNSSActivityState();
      25             : 
      26             :   // Call enter/leave when PSM enters a scope during which
      27             :   // shutting down NSS is prohibited.
      28             :   void enter();
      29             :   void leave();
      30             :   // Wait for all activity to stop, and block any other thread on entering
      31             :   // relevant PSM code.
      32             :   PRStatus restrictActivityToCurrentThread();
      33             : 
      34             :   // Go back to normal state.
      35             :   void releaseCurrentThreadActivityRestriction();
      36             : 
      37             : private:
      38             :   // The lock protecting all our member variables.
      39             :   mozilla::Mutex mNSSActivityStateLock;
      40             : 
      41             :   // The activity variable, bound to our lock,
      42             :   // used either to signal the activity counter reaches zero,
      43             :   // or a thread restriction has been released.
      44             :   mozilla::CondVar mNSSActivityChanged;
      45             : 
      46             :   // The number of active scopes holding resources.
      47             :   int mNSSActivityCounter;
      48             : 
      49             :   // nullptr means "no restriction"
      50             :   // if not null, activity is only allowed on that thread
      51             :   PRThread* mNSSRestrictedThread;
      52             : };
      53             : 
      54             : // Helper class that automatically enters/leaves the global activity state
      55             : class nsNSSShutDownPreventionLock
      56             : {
      57             : public:
      58             :   nsNSSShutDownPreventionLock();
      59             :   ~nsNSSShutDownPreventionLock();
      60             : };
      61             : 
      62             : // Singleton, used by nsNSSComponent to track the list of PSM objects,
      63             : // which hold NSS resources and support the "early cleanup mechanism".
      64             : class nsNSSShutDownList
      65             : {
      66             : public:
      67             :   // track instances that support early cleanup
      68             :   static void remember(nsNSSShutDownObject *o);
      69             :   static void forget(nsNSSShutDownObject *o);
      70             : 
      71             :   // track instances that would like notification when
      72             :   // a PK11 logout operation is performed.
      73             :   static void remember(nsOnPK11LogoutCancelObject *o);
      74             :   static void forget(nsOnPK11LogoutCancelObject *o);
      75             : 
      76             :   // Release all tracked NSS resources and prevent nsNSSShutDownObjects from
      77             :   // using NSS functions.
      78             :   static nsresult evaporateAllNSSResourcesAndShutDown();
      79             : 
      80             :   // PSM has been asked to log out of a token.
      81             :   // Notify all registered instances that want to react to that event.
      82             :   static nsresult doPK11Logout();
      83             : 
      84             :   // Signal entering/leaving a scope where shutting down NSS is prohibited.
      85             :   static void enterActivityState();
      86             :   static void leaveActivityState();
      87             : 
      88             : private:
      89             :   static bool construct(const mozilla::StaticMutexAutoLock& /*proofOfLock*/);
      90             : 
      91             :   nsNSSShutDownList();
      92             :   ~nsNSSShutDownList();
      93             : 
      94             : protected:
      95             :   PLDHashTable mObjects;
      96             :   PLDHashTable mPK11LogoutCancelObjects;
      97             :   nsNSSActivityState mActivityState;
      98             : };
      99             : 
     100             : /*
     101             :   A class deriving from nsNSSShutDownObject will have its instances
     102             :   automatically tracked in a list. However, it must follow some rules
     103             :   to assure correct behaviour.
     104             : 
     105             :   The tricky part is that it is not possible to call virtual
     106             :   functions from a destructor.
     107             : 
     108             :   The deriving class must override virtualDestroyNSSReference().
     109             :   Within this function, it should clean up all resources held to NSS.
     110             :   The function will be called by the global list, if it is time to
     111             :   shut down NSS before all references have been freed.
     112             : 
     113             :   The same code that goes into virtualDestroyNSSReference must
     114             :   also be called from the destructor of the deriving class,
     115             :   which is the standard cleanup (not called from the tracking list).
     116             : 
     117             :   Because of that duplication, it is suggested to implement a
     118             :   function destructorSafeDestroyNSSReference() in the deriving
     119             :   class, and make the implementation of virtualDestroyNSSReference()
     120             :   call destructorSafeDestroyNSSReference().
     121             : 
     122             :   The destructor of the derived class must prevent NSS shutdown on
     123             :   another thread by acquiring an nsNSSShutDownPreventionLock. It must
     124             :   then check to see if NSS has already been shut down by calling
     125             :   isAlreadyShutDown(). If NSS has not been shut down, the destructor
     126             :   must then call destructorSafeDestroyNSSReference() and then
     127             :   shutdown(ShutdownCalledFrom::Object). The second call will deregister with
     128             :   the tracking list, to ensure no additional attempt to free the resources
     129             :   will be made.
     130             : 
     131             :   ----------------------------------------------------------------------------
     132             :   IMPORTANT NOTE REGARDING CLASSES THAT IMPLEMENT nsNSSShutDownObject BUT DO
     133             :   NOT DIRECTLY HOLD NSS RESOURCES:
     134             :   ----------------------------------------------------------------------------
     135             :   Currently, classes that do not hold NSS resources but do call NSS functions
     136             :   inherit from nsNSSShutDownObject (and use the lock/isAlreadyShutDown
     137             :   mechanism) as a way of ensuring it is safe to call those functions. Because
     138             :   these classes do not hold any resources, however, it is tempting to skip the
     139             :   destructor component of this interface. This MUST NOT be done, because
     140             :   if an object of such a class is destructed before the nsNSSShutDownList
     141             :   processes all of its entries, this essentially causes a use-after-free when
     142             :   nsNSSShutDownList reaches the entry that has been destroyed. The safe way to
     143             :   do this is to implement the destructor as usual but omit the call to
     144             :   destructorSafeDestroyNSSReference() as it is unnecessary and probably isn't
     145             :   defined for that class.
     146             : 
     147             :   destructorSafeDestroyNSSReference() does not need to acquire an
     148             :   nsNSSShutDownPreventionLock or check isAlreadyShutDown() as long as it
     149             :   is only called by the destructor that has already acquired the lock and
     150             :   checked for shutdown or by the NSS shutdown code itself (which acquires
     151             :   the same lock and checks if objects it cleans up have already cleaned
     152             :   up themselves).
     153             : 
     154             :   destructorSafeDestroyNSSReference() MUST NOT cause any other
     155             :   nsNSSShutDownObject to be deconstructed. Doing so can cause
     156             :   unsupported concurrent operations on the hash table in the
     157             :   nsNSSShutDownList.
     158             : 
     159             :   class derivedClass : public nsISomeInterface,
     160             :                        public nsNSSShutDownObject
     161             :   {
     162             :     virtual void virtualDestroyNSSReference()
     163             :     {
     164             :       destructorSafeDestroyNSSReference();
     165             :     }
     166             : 
     167             :     void destructorSafeDestroyNSSReference()
     168             :     {
     169             :       // clean up all NSS resources here
     170             :     }
     171             : 
     172             :     virtual ~derivedClass()
     173             :     {
     174             :       nsNSSShutDownPreventionLock locker;
     175             :       if (isAlreadyShutDown()) {
     176             :         return;
     177             :       }
     178             :       destructorSafeDestroyNSSReference();
     179             :       shutdown(ShutdownCalledFrom::Object);
     180             :     }
     181             : 
     182             :     NS_IMETHODIMP doSomething()
     183             :     {
     184             :       if (isAlreadyShutDown())
     185             :         return NS_ERROR_NOT_AVAILABLE;
     186             : 
     187             :       // use the NSS resources and do something
     188             :     }
     189             :   };
     190             : */
     191             : 
     192             : class nsNSSShutDownObject
     193             : {
     194             : public:
     195             :   enum class ShutdownCalledFrom {
     196             :     List,
     197             :     Object,
     198             :   };
     199             : 
     200          17 :   nsNSSShutDownObject()
     201          17 :   {
     202          17 :     mAlreadyShutDown = false;
     203          17 :     nsNSSShutDownList::remember(this);
     204          17 :   }
     205             : 
     206          13 :   virtual ~nsNSSShutDownObject()
     207          13 :   {
     208             :     // The derived class must call
     209             :     //   shutdown(ShutdownCalledFrom::Object);
     210             :     // in its destructor
     211          13 :   }
     212             : 
     213          13 :   void shutdown(ShutdownCalledFrom calledFrom)
     214             :   {
     215          13 :     if (!mAlreadyShutDown) {
     216          13 :       switch (calledFrom) {
     217             :         case ShutdownCalledFrom::Object:
     218          13 :           nsNSSShutDownList::forget(this);
     219          13 :           break;
     220             :         case ShutdownCalledFrom::List:
     221           0 :           virtualDestroyNSSReference();
     222           0 :           break;
     223             :         default:
     224           0 :           MOZ_CRASH("shutdown() called from an unknown source");
     225             :       }
     226          13 :       mAlreadyShutDown = true;
     227             :     }
     228          13 :   }
     229             : 
     230             :   bool isAlreadyShutDown() const;
     231             : 
     232             : protected:
     233             :   virtual void virtualDestroyNSSReference() = 0;
     234             : private:
     235             :   volatile bool mAlreadyShutDown;
     236             : };
     237             : 
     238             : class nsOnPK11LogoutCancelObject
     239             : {
     240             : public:
     241           0 :   nsOnPK11LogoutCancelObject()
     242           0 :     : mIsLoggedOut(false)
     243             :   {
     244           0 :     nsNSSShutDownList::remember(this);
     245           0 :   }
     246             : 
     247           0 :   virtual ~nsOnPK11LogoutCancelObject()
     248           0 :   {
     249           0 :     nsNSSShutDownList::forget(this);
     250           0 :   }
     251             : 
     252           0 :   void logout()
     253             :   {
     254             :     // We do not care for a race condition.
     255             :     // Once the bool arrived at false,
     256             :     // later calls to isPK11LoggedOut() will see it.
     257             :     // This is a one-time change from 0 to 1.
     258           0 :     mIsLoggedOut = true;
     259           0 :   }
     260             : 
     261           0 :   bool isPK11LoggedOut()
     262             :   {
     263           0 :     return mIsLoggedOut;
     264             :   }
     265             : 
     266             : private:
     267             :   volatile bool mIsLoggedOut;
     268             : };
     269             : 
     270             : #endif // nsNSSShutDown_h

Generated by: LCOV version 1.13