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