bug 1239609 - audit nsNSSShutDownObject destructors for correctness r?Cykesiopka r?sworkman
--- a/dom/wifi/WifiCertService.cpp
+++ b/dom/wifi/WifiCertService.cpp
@@ -434,16 +434,22 @@ WifiCertService::WifiCertService()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!gWifiCertService);
}
WifiCertService::~WifiCertService()
{
MOZ_ASSERT(!gWifiCertService);
+
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return;
+ }
+ shutdown(calledFromObject);
}
already_AddRefed<WifiCertService>
WifiCertService::FactoryCreate()
{
if (!XRE_IsParentProcess()) {
return nullptr;
}
--- a/netwerk/base/BackgroundFileSaver.cpp
+++ b/netwerk/base/BackgroundFileSaver.cpp
@@ -1207,16 +1207,20 @@ DigestOutputStream::DigestOutputStream(n
, mDigestContext(aContext)
{
MOZ_ASSERT(mDigestContext, "Can't have null digest context");
MOZ_ASSERT(mOutputStream, "Can't have null output stream");
}
DigestOutputStream::~DigestOutputStream()
{
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return;
+ }
shutdown(calledFromObject);
}
NS_IMETHODIMP
DigestOutputStream::Close()
{
return mOutputStream->Close();
}
--- a/security/manager/ssl/nsKeyModule.cpp
+++ b/security/manager/ssl/nsKeyModule.cpp
@@ -94,16 +94,25 @@ nsKeyObject::GetType(int16_t *_retval)
// nsIKeyObjectFactory
NS_IMPL_ISUPPORTS(nsKeyObjectFactory, nsIKeyObjectFactory)
nsKeyObjectFactory::nsKeyObjectFactory()
{
}
+nsKeyObjectFactory::~nsKeyObjectFactory()
+{
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return;
+ }
+ shutdown(calledFromObject);
+}
+
NS_IMETHODIMP
nsKeyObjectFactory::KeyFromString(int16_t aAlgorithm, const nsACString& aKey,
nsIKeyObject** _retval)
{
if (!_retval || aAlgorithm != nsIKeyObject::HMAC) {
return NS_ERROR_INVALID_ARG;
}
--- a/security/manager/ssl/nsKeyModule.h
+++ b/security/manager/ssl/nsKeyModule.h
@@ -46,17 +46,17 @@ class nsKeyObjectFactory final : public
{
public:
nsKeyObjectFactory();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIKEYOBJECTFACTORY
private:
- ~nsKeyObjectFactory() {}
+ ~nsKeyObjectFactory();
// Disallow copy constructor
nsKeyObjectFactory(nsKeyObjectFactory&);
// No NSS resources to release.
virtual void virtualDestroyNSSReference() override {}
};
--- a/security/manager/ssl/nsNSSShutDown.h
+++ b/security/manager/ssl/nsNSSShutDown.h
@@ -124,16 +124,32 @@ protected:
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(calledFromObject). 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