security/manager/ssl/src/nsNSSShutDown.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/manager/ssl/src/nsNSSShutDown.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,315 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#ifndef _INC_NSSShutDown_H
     1.9 +#define _INC_NSSShutDown_H
    1.10 +
    1.11 +#include "nscore.h"
    1.12 +#include "nspr.h"
    1.13 +#include "pldhash.h"
    1.14 +#include "mozilla/CondVar.h"
    1.15 +#include "mozilla/Mutex.h"
    1.16 +
    1.17 +class nsNSSShutDownObject;
    1.18 +class nsOnPK11LogoutCancelObject;
    1.19 +
    1.20 +// Singleton, owner by nsNSSShutDownList
    1.21 +class nsNSSActivityState
    1.22 +{
    1.23 +public:
    1.24 +  nsNSSActivityState();
    1.25 +  ~nsNSSActivityState();
    1.26 +
    1.27 +  // Call enter/leave when PSM enters a scope during which
    1.28 +  // shutting down NSS is prohibited.
    1.29 +  void enter();
    1.30 +  void leave();
    1.31 +  
    1.32 +  // Call enter/leave when PSM is about to show a UI
    1.33 +  // while still holding resources.
    1.34 +  void enterBlockingUIState();
    1.35 +  void leaveBlockingUIState();
    1.36 +  
    1.37 +  // Is the activity aware of any blocking PSM UI currently shown?
    1.38 +  bool isBlockingUIActive();
    1.39 +
    1.40 +  // Is it forbidden to bring up an UI while holding resources?
    1.41 +  bool isUIForbidden();
    1.42 +  
    1.43 +  // Check whether setting the current thread restriction is possible.
    1.44 +  // If it is possible, and the "do_it_for_real" flag is used,
    1.45 +  // the state tracking will have ensured that we will stay in this state.
    1.46 +  // As of writing, this includes forbidding PSM UI.
    1.47 +  enum RealOrTesting {test_only, do_it_for_real};
    1.48 +  bool ifPossibleDisallowUI(RealOrTesting rot);
    1.49 +
    1.50 +  // Notify the state tracking that going to the restricted state is
    1.51 +  // no longer planned.
    1.52 +  // As of writing, this includes clearing the "PSM UI forbidden" flag.
    1.53 +  void allowUI();
    1.54 +
    1.55 +  // If currently no UI is shown, wait for all activity to stop,
    1.56 +  // and block any other thread on entering relevant PSM code.
    1.57 +  PRStatus restrictActivityToCurrentThread();
    1.58 +  
    1.59 +  // Go back to normal state.
    1.60 +  void releaseCurrentThreadActivityRestriction();
    1.61 +
    1.62 +private:
    1.63 +  // The lock protecting all our member variables.
    1.64 +  mozilla::Mutex mNSSActivityStateLock;
    1.65 +
    1.66 +  // The activity variable, bound to our lock,
    1.67 +  // used either to signal the activity counter reaches zero,
    1.68 +  // or a thread restriction has been released.
    1.69 +  mozilla::CondVar mNSSActivityChanged;
    1.70 +
    1.71 +  // The number of active scopes holding resources.
    1.72 +  int mNSSActivityCounter;
    1.73 +
    1.74 +  // The number of scopes holding resources while blocked
    1.75 +  // showing an UI.
    1.76 +  int mBlockingUICounter;
    1.77 +
    1.78 +  // Whether bringing up UI is currently forbidden
    1.79 +  bool mIsUIForbidden;
    1.80 +
    1.81 +  // nullptr means "no restriction"
    1.82 +  // if not null, activity is only allowed on that thread
    1.83 +  PRThread* mNSSRestrictedThread;
    1.84 +};
    1.85 +
    1.86 +// Helper class that automatically enters/leaves the global activity state
    1.87 +class nsNSSShutDownPreventionLock
    1.88 +{
    1.89 +public:
    1.90 +  nsNSSShutDownPreventionLock();
    1.91 +  ~nsNSSShutDownPreventionLock();
    1.92 +};
    1.93 +
    1.94 +// Helper class that automatically enters/leaves the global UI tracking
    1.95 +class nsPSMUITracker
    1.96 +{
    1.97 +public:
    1.98 +  nsPSMUITracker();
    1.99 +  ~nsPSMUITracker();
   1.100 +  
   1.101 +  bool isUIForbidden();
   1.102 +};
   1.103 +
   1.104 +// Singleton, used by nsNSSComponent to track the list of PSM objects,
   1.105 +// which hold NSS resources and support the "early cleanup mechanism".
   1.106 +class nsNSSShutDownList
   1.107 +{
   1.108 +public:
   1.109 +  ~nsNSSShutDownList();
   1.110 +
   1.111 +  static nsNSSShutDownList *construct();
   1.112 +  
   1.113 +  // track instances that support early cleanup
   1.114 +  static void remember(nsNSSShutDownObject *o);
   1.115 +  static void forget(nsNSSShutDownObject *o);
   1.116 +
   1.117 +  // track instances that would like notification when
   1.118 +  // a PK11 logout operation is performed.
   1.119 +  static void remember(nsOnPK11LogoutCancelObject *o);
   1.120 +  static void forget(nsOnPK11LogoutCancelObject *o);
   1.121 +
   1.122 +  // track the creation and destruction of SSL sockets
   1.123 +  // performed by clients using PSM services
   1.124 +  static void trackSSLSocketCreate();
   1.125 +  static void trackSSLSocketClose();
   1.126 +  static bool areSSLSocketsActive();
   1.127 +  
   1.128 +  // Are we able to do the early cleanup?
   1.129 +  // Returns failure if at the current time "early cleanup" is not possible.
   1.130 +  bool isUIActive();
   1.131 +
   1.132 +  // If possible to do "early cleanup" at the current time, remember that we want to
   1.133 +  // do it, and disallow actions that would change the possibility.
   1.134 +  bool ifPossibleDisallowUI();
   1.135 +
   1.136 +  // Notify that it is no longer planned to do the "early cleanup".
   1.137 +  void allowUI();
   1.138 +  
   1.139 +  // Do the "early cleanup", if possible.
   1.140 +  nsresult evaporateAllNSSResources();
   1.141 +
   1.142 +  // PSM has been asked to log out of a token.
   1.143 +  // Notify all registered instances that want to react to that event.
   1.144 +  nsresult doPK11Logout();
   1.145 +  
   1.146 +  static nsNSSActivityState *getActivityState()
   1.147 +  {
   1.148 +    return singleton ? &singleton->mActivityState : nullptr;
   1.149 +  }
   1.150 +  
   1.151 +private:
   1.152 +  nsNSSShutDownList();
   1.153 +  static PLDHashOperator
   1.154 +  evaporateAllNSSResourcesHelper(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.155 +                                                        uint32_t number, void *arg);
   1.156 +
   1.157 +  static PLDHashOperator
   1.158 +  doPK11LogoutHelper(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.159 +                                                    uint32_t number, void *arg);
   1.160 +protected:
   1.161 +  mozilla::Mutex mListLock;
   1.162 +  static nsNSSShutDownList *singleton;
   1.163 +  PLDHashTable mObjects;
   1.164 +  uint32_t mActiveSSLSockets;
   1.165 +  PLDHashTable mPK11LogoutCancelObjects;
   1.166 +  nsNSSActivityState mActivityState;
   1.167 +};
   1.168 +
   1.169 +/*
   1.170 +  A class deriving from nsNSSShutDownObject will have its instances
   1.171 +  automatically tracked in a list. However, it must follow some rules
   1.172 +  to assure correct behaviour.
   1.173 +  
   1.174 +  The tricky part is that it is not possible to call virtual
   1.175 +  functions from a destructor.
   1.176 +  
   1.177 +  The deriving class must override virtualDestroyNSSReference().
   1.178 +  Within this function, it should clean up all resources held to NSS.
   1.179 +  The function will be called by the global list, if it is time to
   1.180 +  shut down NSS before all references have been freed.
   1.181 +
   1.182 +  The same code that goes into virtualDestroyNSSReference must
   1.183 +  also be called from the destructor of the deriving class,
   1.184 +  which is the standard cleanup (not called from the tracking list).
   1.185 +
   1.186 +  Because of that duplication, it is suggested to implement a
   1.187 +  function destructorSafeDestroyNSSReference() in the deriving
   1.188 +  class, and make the implementation of virtualDestroyNSSReference()
   1.189 +  call destructorSafeDestroyNSSReference().
   1.190 +
   1.191 +  The destructor of the derived class must prevent NSS shutdown on
   1.192 +  another thread by acquiring an nsNSSShutDownPreventionLock. It must
   1.193 +  then check to see if NSS has already been shut down by calling
   1.194 +  isAlreadyShutDown(). If NSS has not been shut down, the destructor
   1.195 +  must then call destructorSafeDestroyNSSReference() and then
   1.196 +  shutdown(calledFromObject). The second call will deregister with
   1.197 +  the tracking list, to ensure no additional attempt to free the resources
   1.198 +  will be made.
   1.199 +
   1.200 +  destructorSafeDestroyNSSReference() does not need to acquire an
   1.201 +  nsNSSShutDownPreventionLock or check isAlreadyShutDown() as long as it
   1.202 +  is only called by the destructor that has already acquired the lock and
   1.203 +  checked for shutdown or by the NSS shutdown code itself (which acquires
   1.204 +  the same lock and checks if objects it cleans up have already cleaned
   1.205 +  up themselves).
   1.206 +
   1.207 +  destructorSafeDestroyNSSReference() MUST NOT cause any other
   1.208 +  nsNSSShutDownObject to be deconstructed. Doing so can cause
   1.209 +  unsupported concurrent operations on the hash table in the
   1.210 +  nsNSSShutDownList.
   1.211 +
   1.212 +  class derivedClass : public nsISomeInterface,
   1.213 +                       public nsNSSShutDownObject
   1.214 +  {
   1.215 +    virtual void virtualDestroyNSSReference()
   1.216 +    {
   1.217 +      destructorSafeDestroyNSSReference();
   1.218 +    }
   1.219 +    
   1.220 +    void destructorSafeDestroyNSSReference()
   1.221 +    {
   1.222 +      // clean up all NSS resources here
   1.223 +    }
   1.224 +
   1.225 +    virtual ~derivedClass()
   1.226 +    {
   1.227 +      nsNSSShutDownPreventionLock locker;
   1.228 +      if (isAlreadyShutDown()) {
   1.229 +        return;
   1.230 +      }
   1.231 +      destructorSafeDestroyNSSReference();
   1.232 +      shutdown(calledFromObject);
   1.233 +    }
   1.234 +    
   1.235 +    NS_IMETHODIMP doSomething()
   1.236 +    {
   1.237 +      if (isAlreadyShutDown())
   1.238 +        return NS_ERROR_NOT_AVAILABLE;
   1.239 +      
   1.240 +      // use the NSS resources and do something
   1.241 +    }
   1.242 +  };
   1.243 +*/
   1.244 +
   1.245 +class nsNSSShutDownObject
   1.246 +{
   1.247 +public:
   1.248 +
   1.249 +  enum CalledFromType {calledFromList, calledFromObject};
   1.250 +
   1.251 +  nsNSSShutDownObject()
   1.252 +  {
   1.253 +    mAlreadyShutDown = false;
   1.254 +    nsNSSShutDownList::remember(this);
   1.255 +  }
   1.256 +  
   1.257 +  virtual ~nsNSSShutDownObject()
   1.258 +  {
   1.259 +    // the derived class must call 
   1.260 +    //   shutdown(calledFromObject);
   1.261 +    // in its destructor
   1.262 +  }
   1.263 +  
   1.264 +  void shutdown(CalledFromType calledFrom)
   1.265 +  {
   1.266 +    if (!mAlreadyShutDown) {
   1.267 +      if (calledFromObject == calledFrom) {
   1.268 +        nsNSSShutDownList::forget(this);
   1.269 +      }
   1.270 +      if (calledFromList == calledFrom) {
   1.271 +        virtualDestroyNSSReference();
   1.272 +      }
   1.273 +      mAlreadyShutDown = true;
   1.274 +    }
   1.275 +  }
   1.276 +  
   1.277 +  bool isAlreadyShutDown() { return mAlreadyShutDown; }
   1.278 +
   1.279 +protected:
   1.280 +  virtual void virtualDestroyNSSReference() = 0;
   1.281 +private:
   1.282 +  volatile bool mAlreadyShutDown;
   1.283 +};
   1.284 +
   1.285 +class nsOnPK11LogoutCancelObject
   1.286 +{
   1.287 +public:
   1.288 +  nsOnPK11LogoutCancelObject()
   1.289 +  :mIsLoggedOut(false)
   1.290 +  {
   1.291 +    nsNSSShutDownList::remember(this);
   1.292 +  }
   1.293 +  
   1.294 +  virtual ~nsOnPK11LogoutCancelObject()
   1.295 +  {
   1.296 +    nsNSSShutDownList::forget(this);
   1.297 +  }
   1.298 +  
   1.299 +  void logout()
   1.300 +  {
   1.301 +    // We do not care for a race condition.
   1.302 +    // Once the bool arrived at false,
   1.303 +    // later calls to isPK11LoggedOut() will see it.
   1.304 +    // This is a one-time change from 0 to 1.
   1.305 +    
   1.306 +    mIsLoggedOut = true;
   1.307 +  }
   1.308 +  
   1.309 +  bool isPK11LoggedOut()
   1.310 +  {
   1.311 +    return mIsLoggedOut;
   1.312 +  }
   1.313 +
   1.314 +private:
   1.315 +  volatile bool mIsLoggedOut;
   1.316 +};
   1.317 +
   1.318 +#endif

mercurial