1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/ssl/src/nsNSSShutDown.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,407 @@ 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 +#include "nsNSSShutDown.h" 1.9 +#include "nsCOMPtr.h" 1.10 + 1.11 +using namespace mozilla; 1.12 + 1.13 +#ifdef PR_LOGGING 1.14 +extern PRLogModuleInfo* gPIPNSSLog; 1.15 +#endif 1.16 + 1.17 +struct ObjectHashEntry : PLDHashEntryHdr { 1.18 + nsNSSShutDownObject *obj; 1.19 +}; 1.20 + 1.21 +static bool 1.22 +ObjectSetMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, 1.23 + const void *key) 1.24 +{ 1.25 + const ObjectHashEntry *entry = static_cast<const ObjectHashEntry*>(hdr); 1.26 + return entry->obj == static_cast<const nsNSSShutDownObject*>(key); 1.27 +} 1.28 + 1.29 +static bool 1.30 +ObjectSetInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.31 + const void *key) 1.32 +{ 1.33 + ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr); 1.34 + entry->obj = const_cast<nsNSSShutDownObject*>(static_cast<const nsNSSShutDownObject*>(key)); 1.35 + return true; 1.36 +} 1.37 + 1.38 +static const PLDHashTableOps gSetOps = { 1.39 + PL_DHashAllocTable, 1.40 + PL_DHashFreeTable, 1.41 + PL_DHashVoidPtrKeyStub, 1.42 + ObjectSetMatchEntry, 1.43 + PL_DHashMoveEntryStub, 1.44 + PL_DHashClearEntryStub, 1.45 + PL_DHashFinalizeStub, 1.46 + ObjectSetInitEntry 1.47 +}; 1.48 + 1.49 +nsNSSShutDownList *nsNSSShutDownList::singleton = nullptr; 1.50 + 1.51 +nsNSSShutDownList::nsNSSShutDownList() 1.52 +:mListLock("nsNSSShutDownList.mListLock") 1.53 +{ 1.54 + mActiveSSLSockets = 0; 1.55 + mPK11LogoutCancelObjects.ops = nullptr; 1.56 + mObjects.ops = nullptr; 1.57 + PL_DHashTableInit(&mObjects, &gSetOps, nullptr, 1.58 + sizeof(ObjectHashEntry), 16); 1.59 + PL_DHashTableInit(&mPK11LogoutCancelObjects, &gSetOps, nullptr, 1.60 + sizeof(ObjectHashEntry), 16); 1.61 +} 1.62 + 1.63 +nsNSSShutDownList::~nsNSSShutDownList() 1.64 +{ 1.65 + if (mObjects.ops) { 1.66 + PL_DHashTableFinish(&mObjects); 1.67 + mObjects.ops = nullptr; 1.68 + } 1.69 + if (mPK11LogoutCancelObjects.ops) { 1.70 + PL_DHashTableFinish(&mPK11LogoutCancelObjects); 1.71 + mPK11LogoutCancelObjects.ops = nullptr; 1.72 + } 1.73 + PR_ASSERT(this == singleton); 1.74 + singleton = nullptr; 1.75 +} 1.76 + 1.77 +void nsNSSShutDownList::remember(nsNSSShutDownObject *o) 1.78 +{ 1.79 + if (!singleton) 1.80 + return; 1.81 + 1.82 + PR_ASSERT(o); 1.83 + MutexAutoLock lock(singleton->mListLock); 1.84 + PL_DHashTableOperate(&singleton->mObjects, o, PL_DHASH_ADD); 1.85 +} 1.86 + 1.87 +void nsNSSShutDownList::forget(nsNSSShutDownObject *o) 1.88 +{ 1.89 + if (!singleton) 1.90 + return; 1.91 + 1.92 + PR_ASSERT(o); 1.93 + MutexAutoLock lock(singleton->mListLock); 1.94 + PL_DHashTableOperate(&singleton->mObjects, o, PL_DHASH_REMOVE); 1.95 +} 1.96 + 1.97 +void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o) 1.98 +{ 1.99 + if (!singleton) 1.100 + return; 1.101 + 1.102 + PR_ASSERT(o); 1.103 + MutexAutoLock lock(singleton->mListLock); 1.104 + PL_DHashTableOperate(&singleton->mPK11LogoutCancelObjects, o, PL_DHASH_ADD); 1.105 +} 1.106 + 1.107 +void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o) 1.108 +{ 1.109 + if (!singleton) 1.110 + return; 1.111 + 1.112 + PR_ASSERT(o); 1.113 + MutexAutoLock lock(singleton->mListLock); 1.114 + PL_DHashTableOperate(&singleton->mPK11LogoutCancelObjects, o, PL_DHASH_REMOVE); 1.115 +} 1.116 + 1.117 +void nsNSSShutDownList::trackSSLSocketCreate() 1.118 +{ 1.119 + if (!singleton) 1.120 + return; 1.121 + 1.122 + MutexAutoLock lock(singleton->mListLock); 1.123 + ++singleton->mActiveSSLSockets; 1.124 +} 1.125 + 1.126 +void nsNSSShutDownList::trackSSLSocketClose() 1.127 +{ 1.128 + if (!singleton) 1.129 + return; 1.130 + 1.131 + MutexAutoLock lock(singleton->mListLock); 1.132 + --singleton->mActiveSSLSockets; 1.133 +} 1.134 + 1.135 +bool nsNSSShutDownList::areSSLSocketsActive() 1.136 +{ 1.137 + if (!singleton) { 1.138 + // I'd rather prefer to be pessimistic and return true. 1.139 + // However, maybe we will get called at a time when the singleton 1.140 + // has already been freed, and returning true would bring up an 1.141 + // unnecessary warning. 1.142 + return false; 1.143 + } 1.144 + 1.145 + MutexAutoLock lock(singleton->mListLock); 1.146 + return (singleton->mActiveSSLSockets > 0); 1.147 +} 1.148 + 1.149 +nsresult nsNSSShutDownList::doPK11Logout() 1.150 +{ 1.151 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("canceling all open SSL sockets to disallow future IO\n")); 1.152 + // During our iteration we will set a bunch of PRBools to true. 1.153 + // Nobody else ever modifies that bool, only we do. 1.154 + // We only must ensure that our objects do not go away. 1.155 + // This is guaranteed by holding the list lock. 1.156 + 1.157 + MutexAutoLock lock(singleton->mListLock); 1.158 + PL_DHashTableEnumerate(&mPK11LogoutCancelObjects, doPK11LogoutHelper, 0); 1.159 + 1.160 + return NS_OK; 1.161 +} 1.162 + 1.163 +PLDHashOperator 1.164 +nsNSSShutDownList::doPK11LogoutHelper(PLDHashTable *table, 1.165 + PLDHashEntryHdr *hdr, uint32_t number, void *arg) 1.166 +{ 1.167 + ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr); 1.168 + 1.169 + nsOnPK11LogoutCancelObject *pklco = 1.170 + reinterpret_cast<nsOnPK11LogoutCancelObject*>(entry->obj); 1.171 + 1.172 + if (pklco) { 1.173 + pklco->logout(); 1.174 + } 1.175 + 1.176 + return PL_DHASH_NEXT; 1.177 +} 1.178 + 1.179 +bool nsNSSShutDownList::isUIActive() 1.180 +{ 1.181 + bool canDisallow = mActivityState.ifPossibleDisallowUI(nsNSSActivityState::test_only); 1.182 + bool bIsUIActive = !canDisallow; 1.183 + return bIsUIActive; 1.184 +} 1.185 + 1.186 +bool nsNSSShutDownList::ifPossibleDisallowUI() 1.187 +{ 1.188 + bool isNowDisallowed = mActivityState.ifPossibleDisallowUI(nsNSSActivityState::do_it_for_real); 1.189 + return isNowDisallowed; 1.190 +} 1.191 + 1.192 +void nsNSSShutDownList::allowUI() 1.193 +{ 1.194 + mActivityState.allowUI(); 1.195 +} 1.196 + 1.197 +nsresult nsNSSShutDownList::evaporateAllNSSResources() 1.198 +{ 1.199 + if (PR_SUCCESS != mActivityState.restrictActivityToCurrentThread()) { 1.200 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("failed to restrict activity to current thread\n")); 1.201 + return NS_ERROR_FAILURE; 1.202 + } 1.203 + 1.204 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("now evaporating NSS resources\n")); 1.205 + int removedCount; 1.206 + do { 1.207 + MutexAutoLock lock(mListLock); 1.208 + removedCount = PL_DHashTableEnumerate(&mObjects, evaporateAllNSSResourcesHelper, 0); 1.209 + } while (removedCount > 0); 1.210 + 1.211 + mActivityState.releaseCurrentThreadActivityRestriction(); 1.212 + return NS_OK; 1.213 +} 1.214 + 1.215 +PLDHashOperator 1.216 +nsNSSShutDownList::evaporateAllNSSResourcesHelper(PLDHashTable *table, 1.217 + PLDHashEntryHdr *hdr, uint32_t number, void *arg) 1.218 +{ 1.219 + ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr); 1.220 + { 1.221 + MutexAutoUnlock unlock(singleton->mListLock); 1.222 + entry->obj->shutdown(nsNSSShutDownObject::calledFromList); 1.223 + } 1.224 + // Never free more than one entry, because other threads might be calling 1.225 + // us and remove themselves while we are iterating over the list, 1.226 + // and the behaviour of changing the list while iterating is undefined. 1.227 + return (PLDHashOperator)(PL_DHASH_STOP | PL_DHASH_REMOVE); 1.228 +} 1.229 + 1.230 +nsNSSShutDownList *nsNSSShutDownList::construct() 1.231 +{ 1.232 + if (singleton) { 1.233 + // we should never ever be called twice 1.234 + return nullptr; 1.235 + } 1.236 + 1.237 + singleton = new nsNSSShutDownList(); 1.238 + return singleton; 1.239 +} 1.240 + 1.241 +nsNSSActivityState::nsNSSActivityState() 1.242 +:mNSSActivityStateLock("nsNSSActivityState.mNSSActivityStateLock"), 1.243 + mNSSActivityChanged(mNSSActivityStateLock, 1.244 + "nsNSSActivityState.mNSSActivityStateLock"), 1.245 + mNSSActivityCounter(0), 1.246 + mBlockingUICounter(0), 1.247 + mIsUIForbidden(false), 1.248 + mNSSRestrictedThread(nullptr) 1.249 +{ 1.250 +} 1.251 + 1.252 +nsNSSActivityState::~nsNSSActivityState() 1.253 +{ 1.254 +} 1.255 + 1.256 +void nsNSSActivityState::enter() 1.257 +{ 1.258 + MutexAutoLock lock(mNSSActivityStateLock); 1.259 + 1.260 + while (mNSSRestrictedThread && mNSSRestrictedThread != PR_GetCurrentThread()) { 1.261 + mNSSActivityChanged.Wait(); 1.262 + } 1.263 + 1.264 + ++mNSSActivityCounter; 1.265 +} 1.266 + 1.267 +void nsNSSActivityState::leave() 1.268 +{ 1.269 + MutexAutoLock lock(mNSSActivityStateLock); 1.270 + 1.271 + --mNSSActivityCounter; 1.272 + 1.273 + mNSSActivityChanged.NotifyAll(); 1.274 +} 1.275 + 1.276 +void nsNSSActivityState::enterBlockingUIState() 1.277 +{ 1.278 + MutexAutoLock lock(mNSSActivityStateLock); 1.279 + 1.280 + ++mBlockingUICounter; 1.281 +} 1.282 + 1.283 +void nsNSSActivityState::leaveBlockingUIState() 1.284 +{ 1.285 + MutexAutoLock lock(mNSSActivityStateLock); 1.286 + 1.287 + --mBlockingUICounter; 1.288 +} 1.289 + 1.290 +bool nsNSSActivityState::isBlockingUIActive() 1.291 +{ 1.292 + MutexAutoLock lock(mNSSActivityStateLock); 1.293 + return (mBlockingUICounter > 0); 1.294 +} 1.295 + 1.296 +bool nsNSSActivityState::isUIForbidden() 1.297 +{ 1.298 + MutexAutoLock lock(mNSSActivityStateLock); 1.299 + return mIsUIForbidden; 1.300 +} 1.301 + 1.302 +bool nsNSSActivityState::ifPossibleDisallowUI(RealOrTesting rot) 1.303 +{ 1.304 + bool retval = false; 1.305 + MutexAutoLock lock(mNSSActivityStateLock); 1.306 + 1.307 + // Checking and disallowing the UI must be done atomically. 1.308 + 1.309 + if (!mBlockingUICounter) { 1.310 + // No UI is currently shown, we are able to evaporate. 1.311 + retval = true; 1.312 + if (rot == do_it_for_real) { 1.313 + // Remember to disallow UI. 1.314 + mIsUIForbidden = true; 1.315 + 1.316 + // to clear the "forbidden" state, 1.317 + // one must either call 1.318 + // restrictActivityToCurrentThread() + releaseCurrentThreadActivityRestriction() 1.319 + // or cancel the operation by calling 1.320 + // unprepareCurrentThreadRestriction() 1.321 + } 1.322 + } 1.323 + return retval; 1.324 +} 1.325 + 1.326 +void nsNSSActivityState::allowUI() 1.327 +{ 1.328 + MutexAutoLock lock(mNSSActivityStateLock); 1.329 + 1.330 + mIsUIForbidden = false; 1.331 +} 1.332 + 1.333 +PRStatus nsNSSActivityState::restrictActivityToCurrentThread() 1.334 +{ 1.335 + PRStatus retval = PR_FAILURE; 1.336 + MutexAutoLock lock(mNSSActivityStateLock); 1.337 + 1.338 + if (!mBlockingUICounter) { 1.339 + while (0 < mNSSActivityCounter && !mBlockingUICounter) { 1.340 + mNSSActivityChanged.Wait(PR_TicksPerSecond()); 1.341 + } 1.342 + 1.343 + if (mBlockingUICounter) { 1.344 + // This should never happen. 1.345 + // If we arrive here, our logic is broken. 1.346 + PR_ASSERT(0); 1.347 + } 1.348 + else { 1.349 + mNSSRestrictedThread = PR_GetCurrentThread(); 1.350 + retval = PR_SUCCESS; 1.351 + } 1.352 + } 1.353 + 1.354 + return retval; 1.355 +} 1.356 + 1.357 +void nsNSSActivityState::releaseCurrentThreadActivityRestriction() 1.358 +{ 1.359 + MutexAutoLock lock(mNSSActivityStateLock); 1.360 + 1.361 + mNSSRestrictedThread = nullptr; 1.362 + mIsUIForbidden = false; 1.363 + 1.364 + mNSSActivityChanged.NotifyAll(); 1.365 +} 1.366 + 1.367 +nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock() 1.368 +{ 1.369 + nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); 1.370 + if (!state) 1.371 + return; 1.372 + 1.373 + state->enter(); 1.374 +} 1.375 + 1.376 +nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock() 1.377 +{ 1.378 + nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); 1.379 + if (!state) 1.380 + return; 1.381 + 1.382 + state->leave(); 1.383 +} 1.384 + 1.385 +nsPSMUITracker::nsPSMUITracker() 1.386 +{ 1.387 + nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); 1.388 + if (!state) 1.389 + return; 1.390 + 1.391 + state->enterBlockingUIState(); 1.392 +} 1.393 + 1.394 +nsPSMUITracker::~nsPSMUITracker() 1.395 +{ 1.396 + nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); 1.397 + if (!state) 1.398 + return; 1.399 + 1.400 + state->leaveBlockingUIState(); 1.401 +} 1.402 + 1.403 +bool nsPSMUITracker::isUIForbidden() 1.404 +{ 1.405 + nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); 1.406 + if (!state) 1.407 + return false; 1.408 + 1.409 + return state->isUIForbidden(); 1.410 +}