summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/nsNSSShutDown.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/nsNSSShutDown.cpp')
-rw-r--r--security/manager/ssl/nsNSSShutDown.cpp288
1 files changed, 288 insertions, 0 deletions
diff --git a/security/manager/ssl/nsNSSShutDown.cpp b/security/manager/ssl/nsNSSShutDown.cpp
new file mode 100644
index 000000000..cffebac1e
--- /dev/null
+++ b/security/manager/ssl/nsNSSShutDown.cpp
@@ -0,0 +1,288 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsNSSShutDown.h"
+
+#include "mozilla/Casting.h"
+#include "nsCOMPtr.h"
+
+using namespace mozilla;
+
+extern LazyLogModule gPIPNSSLog;
+
+struct ObjectHashEntry : PLDHashEntryHdr {
+ nsNSSShutDownObject *obj;
+};
+
+static bool
+ObjectSetMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+{
+ const ObjectHashEntry *entry = static_cast<const ObjectHashEntry*>(hdr);
+ return entry->obj == static_cast<const nsNSSShutDownObject*>(key);
+}
+
+static void
+ObjectSetInitEntry(PLDHashEntryHdr *hdr, const void *key)
+{
+ ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr);
+ entry->obj = const_cast<nsNSSShutDownObject*>(static_cast<const nsNSSShutDownObject*>(key));
+}
+
+static const PLDHashTableOps gSetOps = {
+ PLDHashTable::HashVoidPtrKeyStub,
+ ObjectSetMatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ ObjectSetInitEntry
+};
+
+StaticMutex sListLock;
+Atomic<bool> sInShutdown(false);
+nsNSSShutDownList *singleton = nullptr;
+
+nsNSSShutDownList::nsNSSShutDownList()
+ : mObjects(&gSetOps, sizeof(ObjectHashEntry))
+ , mPK11LogoutCancelObjects(&gSetOps, sizeof(ObjectHashEntry))
+{
+}
+
+nsNSSShutDownList::~nsNSSShutDownList()
+{
+ PR_ASSERT(this == singleton);
+ singleton = nullptr;
+}
+
+void nsNSSShutDownList::remember(nsNSSShutDownObject *o)
+{
+ StaticMutexAutoLock lock(sListLock);
+ if (!nsNSSShutDownList::construct(lock)) {
+ return;
+ }
+
+ PR_ASSERT(o);
+ singleton->mObjects.Add(o, fallible);
+}
+
+void nsNSSShutDownList::forget(nsNSSShutDownObject *o)
+{
+ StaticMutexAutoLock lock(sListLock);
+ if (!singleton) {
+ return;
+ }
+
+ PR_ASSERT(o);
+ singleton->mObjects.Remove(o);
+}
+
+void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o)
+{
+ StaticMutexAutoLock lock(sListLock);
+ if (!nsNSSShutDownList::construct(lock)) {
+ return;
+ }
+
+ PR_ASSERT(o);
+ singleton->mPK11LogoutCancelObjects.Add(o, fallible);
+}
+
+void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o)
+{
+ StaticMutexAutoLock lock(sListLock);
+ if (!singleton) {
+ return;
+ }
+
+ PR_ASSERT(o);
+ singleton->mPK11LogoutCancelObjects.Remove(o);
+}
+
+nsresult nsNSSShutDownList::doPK11Logout()
+{
+ StaticMutexAutoLock lock(sListLock);
+ if (!singleton) {
+ return NS_OK;
+ }
+
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+ ("canceling all open SSL sockets to disallow future IO\n"));
+
+ // During our iteration we will set a bunch of PRBools to true.
+ // Nobody else ever modifies that bool, only we do.
+ // We only must ensure that our objects do not go away.
+ // This is guaranteed by holding the list lock.
+
+ for (auto iter = singleton->mPK11LogoutCancelObjects.Iter();
+ !iter.Done();
+ iter.Next()) {
+ auto entry = static_cast<ObjectHashEntry*>(iter.Get());
+ nsOnPK11LogoutCancelObject* pklco =
+ BitwiseCast<nsOnPK11LogoutCancelObject*, nsNSSShutDownObject*>(entry->obj);
+ if (pklco) {
+ pklco->logout();
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsNSSShutDownList::evaporateAllNSSResources()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ StaticMutexAutoLock lock(sListLock);
+ // Other threads can acquire an nsNSSShutDownPreventionLock and cause this
+ // thread to block when it calls restructActivityToCurrentThread, below. If
+ // those other threads then attempt to create an object that must be
+ // remembered by the shut down list, they will call
+ // nsNSSShutDownList::remember, which attempts to acquire sListLock.
+ // Consequently, holding sListLock while we're in
+ // restrictActivityToCurrentThread would result in deadlock. sListLock
+ // protects the singleton, so if we enforce that the singleton only be created
+ // and destroyed on the main thread, and if we similarly enforce that this
+ // function is only called on the main thread, what we can do is check that
+ // the singleton hasn't already gone away and then we don't actually have to
+ // hold sListLock while calling restrictActivityToCurrentThread.
+ if (!singleton) {
+ return NS_OK;
+ }
+
+ {
+ StaticMutexAutoUnlock unlock(sListLock);
+ PRStatus rv = singleton->mActivityState.restrictActivityToCurrentThread();
+ if (rv != PR_SUCCESS) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+ ("failed to restrict activity to current thread"));
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("now evaporating NSS resources"));
+
+ // Never free more than one entry, because other threads might be calling
+ // us and remove themselves while we are iterating over the list,
+ // and the behaviour of changing the list while iterating is undefined.
+ while (singleton) {
+ auto iter = singleton->mObjects.Iter();
+ if (iter.Done()) {
+ break;
+ }
+ auto entry = static_cast<ObjectHashEntry*>(iter.Get());
+ {
+ StaticMutexAutoUnlock unlock(sListLock);
+ entry->obj->shutdown(nsNSSShutDownObject::ShutdownCalledFrom::List);
+ }
+ iter.Remove();
+ }
+
+ if (!singleton) {
+ return NS_ERROR_FAILURE;
+ }
+
+ singleton->mActivityState.releaseCurrentThreadActivityRestriction();
+ return NS_OK;
+}
+
+void nsNSSShutDownList::enterActivityState()
+{
+ StaticMutexAutoLock lock(sListLock);
+ if (nsNSSShutDownList::construct(lock)) {
+ singleton->mActivityState.enter();
+ }
+}
+
+void nsNSSShutDownList::leaveActivityState()
+{
+ StaticMutexAutoLock lock(sListLock);
+ if (singleton) {
+ singleton->mActivityState.leave();
+ }
+}
+
+bool nsNSSShutDownList::construct(const StaticMutexAutoLock& /*proofOfLock*/)
+{
+ if (!singleton && !sInShutdown && XRE_IsParentProcess()) {
+ singleton = new nsNSSShutDownList();
+ }
+
+ return !!singleton;
+}
+
+void nsNSSShutDownList::shutdown()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sListLock);
+ sInShutdown = true;
+
+ if (singleton) {
+ delete singleton;
+ }
+}
+
+nsNSSActivityState::nsNSSActivityState()
+:mNSSActivityStateLock("nsNSSActivityState.mNSSActivityStateLock"),
+ mNSSActivityChanged(mNSSActivityStateLock,
+ "nsNSSActivityState.mNSSActivityStateLock"),
+ mNSSActivityCounter(0),
+ mNSSRestrictedThread(nullptr)
+{
+}
+
+nsNSSActivityState::~nsNSSActivityState()
+{
+}
+
+void nsNSSActivityState::enter()
+{
+ MutexAutoLock lock(mNSSActivityStateLock);
+
+ while (mNSSRestrictedThread && mNSSRestrictedThread != PR_GetCurrentThread()) {
+ mNSSActivityChanged.Wait();
+ }
+
+ ++mNSSActivityCounter;
+}
+
+void nsNSSActivityState::leave()
+{
+ MutexAutoLock lock(mNSSActivityStateLock);
+
+ --mNSSActivityCounter;
+
+ mNSSActivityChanged.NotifyAll();
+}
+
+PRStatus nsNSSActivityState::restrictActivityToCurrentThread()
+{
+ MutexAutoLock lock(mNSSActivityStateLock);
+
+ while (mNSSActivityCounter > 0) {
+ mNSSActivityChanged.Wait(PR_TicksPerSecond());
+ }
+
+ mNSSRestrictedThread = PR_GetCurrentThread();
+
+ return PR_SUCCESS;
+}
+
+void nsNSSActivityState::releaseCurrentThreadActivityRestriction()
+{
+ MutexAutoLock lock(mNSSActivityStateLock);
+
+ mNSSRestrictedThread = nullptr;
+
+ mNSSActivityChanged.NotifyAll();
+}
+
+nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock()
+{
+ nsNSSShutDownList::enterActivityState();
+}
+
+nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock()
+{
+ nsNSSShutDownList::leaveActivityState();
+}