bug 1239609 - audit nsNSSShutDownObject destructors for correctness r?Cykesiopka r?sworkman draft
authorDavid Keeler <dkeeler@mozilla.com>
Fri, 22 Jan 2016 14:49:39 -0800
changeset 326017 9c06645465e9d7ec630f82776a533dd6bb7637ef
parent 325780 c0ba5835ca489d15f8f170d5deb01f8dad92709a
child 513533 21145e6d7e72d3b143ec6680d7d701712c259867
push id10075
push userdkeeler@mozilla.com
push dateTue, 26 Jan 2016 23:30:41 +0000
reviewersCykesiopka, sworkman
bugs1239609
milestone47.0a1
bug 1239609 - audit nsNSSShutDownObject destructors for correctness r?Cykesiopka r?sworkman
dom/wifi/WifiCertService.cpp
netwerk/base/BackgroundFileSaver.cpp
security/manager/ssl/nsKeyModule.cpp
security/manager/ssl/nsKeyModule.h
security/manager/ssl/nsNSSShutDown.h
--- 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