summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/nsNSSShutDown.h
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/nsNSSShutDown.h')
-rw-r--r--security/manager/ssl/nsNSSShutDown.h271
1 files changed, 271 insertions, 0 deletions
diff --git a/security/manager/ssl/nsNSSShutDown.h b/security/manager/ssl/nsNSSShutDown.h
new file mode 100644
index 000000000..00d017239
--- /dev/null
+++ b/security/manager/ssl/nsNSSShutDown.h
@@ -0,0 +1,271 @@
+/* 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/. */
+
+#ifndef nsNSSShutDown_h
+#define nsNSSShutDown_h
+
+#include "PLDHashTable.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticMutex.h"
+#include "nscore.h"
+#include "nspr.h"
+
+class nsNSSShutDownObject;
+class nsOnPK11LogoutCancelObject;
+
+// Singleton, owned by nsNSSShutDownList
+class nsNSSActivityState
+{
+public:
+ nsNSSActivityState();
+ ~nsNSSActivityState();
+
+ // Call enter/leave when PSM enters a scope during which
+ // shutting down NSS is prohibited.
+ void enter();
+ void leave();
+ // Wait for all activity to stop, and block any other thread on entering
+ // relevant PSM code.
+ PRStatus restrictActivityToCurrentThread();
+
+ // Go back to normal state.
+ void releaseCurrentThreadActivityRestriction();
+
+private:
+ // The lock protecting all our member variables.
+ mozilla::Mutex mNSSActivityStateLock;
+
+ // The activity variable, bound to our lock,
+ // used either to signal the activity counter reaches zero,
+ // or a thread restriction has been released.
+ mozilla::CondVar mNSSActivityChanged;
+
+ // The number of active scopes holding resources.
+ int mNSSActivityCounter;
+
+ // nullptr means "no restriction"
+ // if not null, activity is only allowed on that thread
+ PRThread* mNSSRestrictedThread;
+};
+
+// Helper class that automatically enters/leaves the global activity state
+class nsNSSShutDownPreventionLock
+{
+public:
+ nsNSSShutDownPreventionLock();
+ ~nsNSSShutDownPreventionLock();
+};
+
+// Singleton, used by nsNSSComponent to track the list of PSM objects,
+// which hold NSS resources and support the "early cleanup mechanism".
+class nsNSSShutDownList
+{
+public:
+ static void shutdown();
+
+ // track instances that support early cleanup
+ static void remember(nsNSSShutDownObject *o);
+ static void forget(nsNSSShutDownObject *o);
+
+ // track instances that would like notification when
+ // a PK11 logout operation is performed.
+ static void remember(nsOnPK11LogoutCancelObject *o);
+ static void forget(nsOnPK11LogoutCancelObject *o);
+
+ // Do the "early cleanup", if possible.
+ static nsresult evaporateAllNSSResources();
+
+ // PSM has been asked to log out of a token.
+ // Notify all registered instances that want to react to that event.
+ static nsresult doPK11Logout();
+
+ // Signal entering/leaving a scope where shutting down NSS is prohibited.
+ static void enterActivityState();
+ static void leaveActivityState();
+
+private:
+ static bool construct(const mozilla::StaticMutexAutoLock& /*proofOfLock*/);
+
+ nsNSSShutDownList();
+ ~nsNSSShutDownList();
+
+protected:
+ PLDHashTable mObjects;
+ PLDHashTable mPK11LogoutCancelObjects;
+ nsNSSActivityState mActivityState;
+};
+
+/*
+ A class deriving from nsNSSShutDownObject will have its instances
+ automatically tracked in a list. However, it must follow some rules
+ to assure correct behaviour.
+
+ The tricky part is that it is not possible to call virtual
+ functions from a destructor.
+
+ The deriving class must override virtualDestroyNSSReference().
+ Within this function, it should clean up all resources held to NSS.
+ The function will be called by the global list, if it is time to
+ shut down NSS before all references have been freed.
+
+ The same code that goes into virtualDestroyNSSReference must
+ also be called from the destructor of the deriving class,
+ which is the standard cleanup (not called from the tracking list).
+
+ Because of that duplication, it is suggested to implement a
+ function destructorSafeDestroyNSSReference() in the deriving
+ class, and make the implementation of virtualDestroyNSSReference()
+ call destructorSafeDestroyNSSReference().
+
+ The destructor of the derived class must prevent NSS shutdown on
+ another thread by acquiring an nsNSSShutDownPreventionLock. It must
+ then check to see if NSS has already been shut down by calling
+ isAlreadyShutDown(). If NSS has not been shut down, the destructor
+ must then call destructorSafeDestroyNSSReference() and then
+ shutdown(ShutdownCalledFrom::Object). The second call will deregister with
+ the tracking list, to ensure no additional attempt to free the resources
+ will be made.
+
+ ----------------------------------------------------------------------------
+ IMPORTANT NOTE REGARDING CLASSES THAT IMPLEMENT nsNSSShutDownObject BUT DO
+ NOT DIRECTLY HOLD NSS RESOURCES:
+ ----------------------------------------------------------------------------
+ Currently, classes that do not hold NSS resources but do call NSS functions
+ inherit from nsNSSShutDownObject (and use the lock/isAlreadyShutDown
+ mechanism) as a way of ensuring it is safe to call those functions. Because
+ these classes do not hold any resources, however, it is tempting to skip the
+ destructor component of this interface. This MUST NOT be done, because
+ if an object of such a class is destructed before the nsNSSShutDownList
+ processes all of its entries, this essentially causes a use-after-free when
+ nsNSSShutDownList reaches the entry that has been destroyed. The safe way to
+ do this is to implement the destructor as usual but omit the call to
+ destructorSafeDestroyNSSReference() as it is unnecessary and probably isn't
+ defined for that class.
+
+ destructorSafeDestroyNSSReference() does not need to acquire an
+ nsNSSShutDownPreventionLock or check isAlreadyShutDown() as long as it
+ is only called by the destructor that has already acquired the lock and
+ checked for shutdown or by the NSS shutdown code itself (which acquires
+ the same lock and checks if objects it cleans up have already cleaned
+ up themselves).
+
+ destructorSafeDestroyNSSReference() MUST NOT cause any other
+ nsNSSShutDownObject to be deconstructed. Doing so can cause
+ unsupported concurrent operations on the hash table in the
+ nsNSSShutDownList.
+
+ class derivedClass : public nsISomeInterface,
+ public nsNSSShutDownObject
+ {
+ virtual void virtualDestroyNSSReference()
+ {
+ destructorSafeDestroyNSSReference();
+ }
+
+ void destructorSafeDestroyNSSReference()
+ {
+ // clean up all NSS resources here
+ }
+
+ virtual ~derivedClass()
+ {
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return;
+ }
+ destructorSafeDestroyNSSReference();
+ shutdown(ShutdownCalledFrom::Object);
+ }
+
+ NS_IMETHODIMP doSomething()
+ {
+ if (isAlreadyShutDown())
+ return NS_ERROR_NOT_AVAILABLE;
+
+ // use the NSS resources and do something
+ }
+ };
+*/
+
+class nsNSSShutDownObject
+{
+public:
+ enum class ShutdownCalledFrom {
+ List,
+ Object,
+ };
+
+ nsNSSShutDownObject()
+ {
+ mAlreadyShutDown = false;
+ nsNSSShutDownList::remember(this);
+ }
+
+ virtual ~nsNSSShutDownObject()
+ {
+ // The derived class must call
+ // shutdown(ShutdownCalledFrom::Object);
+ // in its destructor
+ }
+
+ void shutdown(ShutdownCalledFrom calledFrom)
+ {
+ if (!mAlreadyShutDown) {
+ switch (calledFrom) {
+ case ShutdownCalledFrom::Object:
+ nsNSSShutDownList::forget(this);
+ break;
+ case ShutdownCalledFrom::List:
+ virtualDestroyNSSReference();
+ break;
+ default:
+ MOZ_CRASH("shutdown() called from an unknown source");
+ }
+ mAlreadyShutDown = true;
+ }
+ }
+
+ bool isAlreadyShutDown() const { return mAlreadyShutDown; }
+
+protected:
+ virtual void virtualDestroyNSSReference() = 0;
+private:
+ volatile bool mAlreadyShutDown;
+};
+
+class nsOnPK11LogoutCancelObject
+{
+public:
+ nsOnPK11LogoutCancelObject()
+ : mIsLoggedOut(false)
+ {
+ nsNSSShutDownList::remember(this);
+ }
+
+ virtual ~nsOnPK11LogoutCancelObject()
+ {
+ nsNSSShutDownList::forget(this);
+ }
+
+ void logout()
+ {
+ // We do not care for a race condition.
+ // Once the bool arrived at false,
+ // later calls to isPK11LoggedOut() will see it.
+ // This is a one-time change from 0 to 1.
+ mIsLoggedOut = true;
+ }
+
+ bool isPK11LoggedOut()
+ {
+ return mIsLoggedOut;
+ }
+
+private:
+ volatile bool mIsLoggedOut;
+};
+
+#endif // nsNSSShutDown_h