bug 1215690 - remove nsPSMUITracker r=Cykesiopka r=mgoodwin
authorDavid Keeler <dkeeler@mozilla.com>
Fri, 16 Oct 2015 14:31:57 -0700
changeset 302500 406f9bce7d238e3d4d63fb83114723d8d7114d90
parent 302499 da5aa88440480550534743d0446d6e0debbe2f23
child 302501 6502eda903ea9389cabea8d645e76e1c52be3b5c
push id5392
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:08:23 +0000
treeherdermozilla-beta@16ce8562a975 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersCykesiopka, mgoodwin
bugs1215690
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1215690 - remove nsPSMUITracker r=Cykesiopka r=mgoodwin nsPSMUITracker was problematic. Apparently it was originally intended to prevent NSS shutdown while NSS-related UI operations were going on (such as choosing a client certificate). However, when nsNSSComponent would receive the event that told it to shutdown NSS, it would attempt to call mShutdownObjectList->evaporateAllNSSResources(), which would call mActivityState.restrictActivityToCurrentThread(), which failed if such a UI operation was in progress. This actually prevented the important part of evaporateAllNSSResources, which is the releasing of all NSS objects in use by PSM objects. Importantly, nsNSSComponent didn't check for or handle this failure and proceeded to call NSS_Shutdown(), leaving PSM in an inconsistent state where it thought it was okay to keep using the NSS objects it had when in fact it wasn't. In any case, nsPSMUITracker isn't really necessary as long as we have the nsNSSShutDownPreventionLock mechanism, which mostly works and is what we should use instead (or not at all, if no such lock is needed for the operation being performed (for example, if no NSS functions are being called)).
security/manager/ssl/nsCertPicker.cpp
security/manager/ssl/nsCertPicker.h
security/manager/ssl/nsKeygenHandler.cpp
security/manager/ssl/nsKeygenHandler.h
security/manager/ssl/nsNSSCallbacks.cpp
security/manager/ssl/nsNSSCertificateDB.cpp
security/manager/ssl/nsNSSComponent.cpp
security/manager/ssl/nsNSSComponent.h
security/manager/ssl/nsNSSHelper.h
security/manager/ssl/nsNSSIOLayer.cpp
security/manager/ssl/nsNSSShutDown.cpp
security/manager/ssl/nsNSSShutDown.h
security/manager/ssl/nsPK11TokenDB.cpp
security/manager/ssl/nsPKCS12Blob.cpp
security/manager/ssl/nsPKCS12Blob.h
security/manager/ssl/nsSDR.cpp
security/manager/ssl/nsSDR.h
--- a/security/manager/ssl/nsCertPicker.cpp
+++ b/security/manager/ssl/nsCertPicker.cpp
@@ -24,28 +24,38 @@ using namespace mozilla;
 NS_IMPL_ISUPPORTS(nsCertPicker, nsIUserCertPicker)
 
 nsCertPicker::nsCertPicker()
 {
 }
 
 nsCertPicker::~nsCertPicker()
 {
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+
+  shutdown(calledFromObject);
 }
 
 NS_IMETHODIMP nsCertPicker::PickByUsage(nsIInterfaceRequestor *ctx, 
                                         const char16_t *selectedNickname, 
                                         int32_t certUsage, 
                                         bool allowInvalid, 
                                         bool allowDuplicateNicknames, 
                                         const nsAString &emailAddress,
                                         bool *canceled, 
                                         nsIX509Cert **_retval)
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   int32_t selectedIndex = -1;
   bool selectionFound = false;
   char16_t **certNicknameList = nullptr;
   char16_t **certDetailsList = nullptr;
   CERTCertListNode* node = nullptr;
   nsresult rv = NS_OK;
 
   {
@@ -153,26 +163,20 @@ NS_IMETHODIMP nsCertPicker::PickByUsage(
 
   if (CertsToUse) {
     nsICertPickDialogs *dialogs = nullptr;
     rv = getNSSDialogs((void**)&dialogs, 
       NS_GET_IID(nsICertPickDialogs), 
       NS_CERTPICKDIALOGS_CONTRACTID);
 
     if (NS_SUCCEEDED(rv)) {
-      nsPSMUITracker tracker;
-      if (tracker.isUIForbidden()) {
-        rv = NS_ERROR_NOT_AVAILABLE;
-      }
-      else {
-        /* Throw up the cert picker dialog and get back the index of the selected cert */
-        rv = dialogs->PickCertificate(ctx,
-          (const char16_t**)certNicknameList, (const char16_t**)certDetailsList,
-          CertsToUse, &selectedIndex, canceled);
-      }
+      // Show the cert picker dialog and get the index of the selected cert.
+      rv = dialogs->PickCertificate(ctx, (const char16_t**)certNicknameList,
+                                    (const char16_t**)certDetailsList,
+                                    CertsToUse, &selectedIndex, canceled);
 
       NS_RELEASE(dialogs);
     }
   }
 
   int32_t i;
   for (i = 0; i < CertsToUse; ++i) {
     free(certNicknameList[i]);
--- a/security/manager/ssl/nsCertPicker.h
+++ b/security/manager/ssl/nsCertPicker.h
@@ -2,25 +2,30 @@
 /* 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 _NSCERTPICKER_H_
 #define _NSCERTPICKER_H_
 
 #include "nsIUserCertPicker.h"
+#include "nsNSSShutDown.h"
 
 #define NS_CERT_PICKER_CID \
   { 0x735959a1, 0xaf01, 0x447e, { 0xb0, 0x2d, 0x56, 0xe9, 0x68, 0xfa, 0x52, 0xb4 } }
 
 class nsCertPicker : public nsIUserCertPicker
+                   , public nsNSSShutDownObject
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIUSERCERTPICKER
 
   nsCertPicker();
 
+  // Nothing to actually release.
+  virtual void virtualDestroyNSSReference() override {}
+
 protected:
   virtual ~nsCertPicker();
 };
 
 #endif //_NSCERTPICKER_H_
--- a/security/manager/ssl/nsKeygenHandler.cpp
+++ b/security/manager/ssl/nsKeygenHandler.cpp
@@ -189,21 +189,26 @@ decode_ec_params(const char *curve)
     return ecparams;
 }
 
 NS_IMPL_ISUPPORTS(nsKeygenFormProcessor, nsIFormProcessor)
 
 nsKeygenFormProcessor::nsKeygenFormProcessor()
 { 
    m_ctx = new PipUIContext();
-
 } 
 
 nsKeygenFormProcessor::~nsKeygenFormProcessor()
 {
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+
+  shutdown(calledFromObject);
 }
 
 nsresult
 nsKeygenFormProcessor::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
 {
   if (GeckoProcessType_Content == XRE_GetProcessType()) {
     nsCOMPtr<nsISupports> contentProcessor = new nsKeygenFormProcessorContent();
     return contentProcessor->QueryInterface(aIID, aResult);
@@ -241,20 +246,24 @@ nsKeygenFormProcessor::Init()
   mSECKeySizeChoiceList[1].size = 1024;
 
   return NS_OK;
 }
 
 nsresult
 nsKeygenFormProcessor::GetSlot(uint32_t aMechanism, PK11SlotInfo** aSlot)
 {
-  return GetSlotWithMechanism(aMechanism,m_ctx,aSlot);
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  return GetSlotWithMechanism(aMechanism, m_ctx, aSlot, locker);
 }
 
-
 uint32_t MapGenMechToAlgoMech(uint32_t mechanism)
 {
     uint32_t searchMech;
 
     /* We are interested in slots based on the ability to perform
        a given algorithm, not on their ability to generate keys usable
        by that algorithm. Therefore, map keygen-specific mechanism tags
        to tags for the corresponding crypto algorithm. */
@@ -279,21 +288,19 @@ uint32_t MapGenMechToAlgoMech(uint32_t m
         searchMech = mechanism;
         break;
     }
     return searchMech;
 }
 
 
 nsresult
-GetSlotWithMechanism(uint32_t aMechanism, 
-                     nsIInterfaceRequestor *m_ctx,
-                     PK11SlotInfo** aSlot)
+GetSlotWithMechanism(uint32_t aMechanism, nsIInterfaceRequestor* m_ctx,
+                     PK11SlotInfo** aSlot, nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
-    nsNSSShutDownPreventionLock locker;
     PK11SlotList * slotList = nullptr;
     char16_t** tokenNameList = nullptr;
     nsITokenDialogs * dialogs;
     char16_t *unicodeTokenChosen;
     PK11SlotListElement *slotElement, *tmpSlot;
     uint32_t numSlots = 0, i = 0;
     bool canceled;
     nsresult rv = NS_OK;
@@ -344,27 +351,21 @@ GetSlotWithMechanism(uint32_t aMechanism
 
 		/* Throw up the token list dialog and get back the token */
 		rv = getNSSDialogs((void**)&dialogs,
 			               NS_GET_IID(nsITokenDialogs),
                      NS_TOKENDIALOGS_CONTRACTID);
 
 		if (NS_FAILED(rv)) goto loser;
 
-    {
-      nsPSMUITracker tracker;
-      if (!tokenNameList || !*tokenNameList) {
-          rv = NS_ERROR_OUT_OF_MEMORY;
-      }
-      else if (tracker.isUIForbidden()) {
-        rv = NS_ERROR_NOT_AVAILABLE;
-      }
-      else {
-        rv = dialogs->ChooseToken(m_ctx, (const char16_t**)tokenNameList, numSlots, &unicodeTokenChosen, &canceled);
-      }
+    if (!tokenNameList || !*tokenNameList) {
+        rv = NS_ERROR_OUT_OF_MEMORY;
+    } else {
+        rv = dialogs->ChooseToken(m_ctx, (const char16_t**)tokenNameList,
+                                  numSlots, &unicodeTokenChosen, &canceled);
     }
 		NS_RELEASE(dialogs);
 		if (NS_FAILED(rv)) goto loser;
 
 		if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
 
         // Get the slot //
         slotElement = PK11_GetFirstSafe(slotList);
@@ -447,16 +448,20 @@ GatherKeygenTelemetry(uint32_t keyGenMec
 nsresult
 nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue,
                                     const nsAString& aChallenge,
                                     const nsAFlatString& aKeyType,
                                     nsAString& aOutPublicKey,
                                     const nsAString& aKeyParams)
 {
     nsNSSShutDownPreventionLock locker;
+    if (isAlreadyShutDown()) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
+
     nsresult rv = NS_ERROR_FAILURE;
     char *keystring = nullptr;
     char *keyparamsString = nullptr;
     uint32_t keyGenMechanism;
     PK11SlotInfo *slot = nullptr;
     PK11RSAGenParams rsaParams;
     SECOidTag algTag;
     int keysize = 0;
@@ -564,17 +569,17 @@ nsKeygenFormProcessor::GetPublicKey(cons
              */
             algTag = SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST;
             break;
       default:
           goto loser;
       }
 
     /* Make sure token is initialized. */
-    rv = setPassword(slot, m_ctx);
+    rv = setPassword(slot, m_ctx, locker);
     if (NS_FAILED(rv))
         goto loser;
 
     sec_rv = PK11_Authenticate(slot, true, m_ctx);
     if (sec_rv != SECSuccess) {
         goto loser;
     }
 
@@ -593,28 +598,20 @@ nsKeygenFormProcessor::GetPublicKey(cons
                                                    &publicKey, attrFlags, m_ctx);
     } else {
         KeygenRunnable->SetParams( slot, attrFlags, nullptr, 0,
                                    keyGenMechanism, params, m_ctx );
 
         runnable = do_QueryInterface(KeygenRunnable);
         
         if (runnable) {
-            {
-              nsPSMUITracker tracker;
-              if (tracker.isUIForbidden()) {
-                rv = NS_ERROR_NOT_AVAILABLE;
-              }
-              else {
-                rv = dialogs->DisplayGeneratingKeypairInfo(m_ctx, runnable);
-                // We call join on the thread, 
-                // so we can be sure that no simultaneous access to the passed parameters will happen.
-                KeygenRunnable->Join();
-              }
-            }
+            rv = dialogs->DisplayGeneratingKeypairInfo(m_ctx, runnable);
+            // We call join on the thread so we can be sure that no
+            // simultaneous access to the passed parameters will happen.
+            KeygenRunnable->Join();
 
             NS_RELEASE(dialogs);
             if (NS_SUCCEEDED(rv)) {
                 PK11SlotInfo *used_slot = nullptr;
                 rv = KeygenRunnable->ConsumeResult(&used_slot, &privateKey, &publicKey);
                 if (NS_SUCCEEDED(rv) && used_slot) {
                   PK11_FreeSlot(used_slot);
                 }
--- a/security/manager/ssl/nsKeygenHandler.h
+++ b/security/manager/ssl/nsKeygenHandler.h
@@ -4,27 +4,31 @@
  * 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 _NSKEYGENHANDLER_H_
 #define _NSKEYGENHANDLER_H_
 // Form Processor
 #include "nsIFormProcessor.h"
 #include "nsTArray.h"
+#include "nsNSSShutDown.h"
 
 nsresult GetSlotWithMechanism(uint32_t mechanism,
-                              nsIInterfaceRequestor *ctx,
-                              PK11SlotInfo **retSlot);
+                              nsIInterfaceRequestor* ctx,
+                              PK11SlotInfo** retSlot,
+                              nsNSSShutDownPreventionLock& /*proofOfLock*/);
 
 #define DEFAULT_RSA_KEYGEN_PE 65537L
 #define DEFAULT_RSA_KEYGEN_ALG SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION
 
 SECKEYECParams *decode_ec_params(const char *curve);
 
-class nsKeygenFormProcessor : public nsIFormProcessor {
+class nsKeygenFormProcessor : public nsIFormProcessor
+                            , public nsNSSShutDownObject
+{
 public:
   nsKeygenFormProcessor();
   nsresult Init();
 
   virtual nsresult ProcessValue(nsIDOMHTMLElement* aElement,
                                 const nsAString& aName,
                                 nsAString& aValue) override;
 
@@ -41,16 +45,19 @@ public:
 
   static nsresult Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult);
 
   static void ExtractParams(nsIDOMHTMLElement* aElement,
                             nsAString& challengeValue,
                             nsAString& keyTypeValue,
                             nsAString& keyParamsValue);
 
+  // Nothing to release.
+  virtual void virtualDestroyNSSReference() override {}
+
 protected:
   virtual ~nsKeygenFormProcessor();
 
   nsresult GetPublicKey(const nsAString& aValue, const nsAString& aChallenge,
                         const nsAFlatString& akeyType, nsAString& aOutPublicKey,
                         const nsAString& aPqg);
   nsresult GetSlot(uint32_t aMechanism, PK11SlotInfo** aSlot);
 private:
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -763,48 +763,62 @@ ShowProtectedAuthPrompt(PK11SlotInfo* sl
 
     NS_RELEASE(dialogs);
   }
 
   return protAuthRetVal;
 }
 
 class PK11PasswordPromptRunnable : public SyncRunnableBase
+                                 , public nsNSSShutDownObject
 {
 public:
   PK11PasswordPromptRunnable(PK11SlotInfo* slot, 
                              nsIInterfaceRequestor* ir)
     : mResult(nullptr),
       mSlot(slot),
       mIR(ir)
   {
   }
+  virtual ~PK11PasswordPromptRunnable();
+
+  // This doesn't own the PK11SlotInfo or any other NSS objects, so there's
+  // nothing to release.
+  virtual void virtualDestroyNSSReference() override {}
   char * mResult; // out
-  virtual void RunOnTargetThread();
+  virtual void RunOnTargetThread() override;
 private:
   PK11SlotInfo* const mSlot; // in
   nsIInterfaceRequestor* const mIR; // in
 };
 
+PK11PasswordPromptRunnable::~PK11PasswordPromptRunnable()
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+
+  shutdown(calledFromObject);
+}
+
 void PK11PasswordPromptRunnable::RunOnTargetThread()
 {
   static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
 
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+
   nsresult rv = NS_OK;
   char16_t *password = nullptr;
   bool value = false;
   nsCOMPtr<nsIPrompt> prompt;
 
-  /* TODO: Retry should generate a different dialog message */
-/*
-  if (retry)
-    return nullptr;
-*/
-
   if (!mIR)
   {
     nsNSSComponent::GetNewPrompter(getter_AddRefs(prompt));
   }
   else
   {
     prompt = do_GetInterface(mIR);
     NS_ASSERTION(prompt, "callbacks does not implement nsIPrompt");
@@ -830,29 +844,21 @@ void PK11PasswordPromptRunnable::RunOnTa
   rv = nssComponent->PIPBundleFormatStringFromName("CertPassPrompt",
                                       formatStrings, 1,
                                       promptString);
   free(const_cast<char16_t*>(formatStrings[0]));
 
   if (NS_FAILED(rv))
     return;
 
-  {
-    nsPSMUITracker tracker;
-    if (tracker.isUIForbidden()) {
-      rv = NS_ERROR_NOT_AVAILABLE;
-    }
-    else {
-      // Although the exact value is ignored, we must not pass invalid
-      // bool values through XPConnect.
-      bool checkState = false;
-      rv = prompt->PromptPassword(nullptr, promptString.get(),
-                                  &password, nullptr, &checkState, &value);
-    }
-  }
+  // Although the exact value is ignored, we must not pass invalid bool values
+  // through XPConnect.
+  bool checkState = false;
+  rv = prompt->PromptPassword(nullptr, promptString.get(), &password, nullptr,
+                              &checkState, &value);
   
   if (NS_SUCCEEDED(rv) && value) {
     mResult = ToNewUTF8String(nsDependentString(password));
     free(password);
   }
 }
 
 char*
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -815,38 +815,36 @@ void nsNSSCertificateDB::DisplayCertific
 {
   static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
 
   if (!NS_IsMainThread()) {
     NS_ERROR("nsNSSCertificateDB::DisplayCertificateAlert called off the main thread");
     return;
   }
 
-  nsPSMUITracker tracker;
-  if (!tracker.isUIForbidden()) {
+  nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
+  if (!my_ctx) {
+    my_ctx = new PipUIContext();
+  }
 
-    nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
-    if (!my_ctx)
-      my_ctx = new PipUIContext();
-
-    // This shall be replaced by embedding ovverridable prompts
-    // as discussed in bug 310446, and should make use of certToShow.
+  // This shall be replaced by embedding ovverridable prompts
+  // as discussed in bug 310446, and should make use of certToShow.
 
-    nsresult rv;
-    nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
-    if (NS_SUCCEEDED(rv)) {
-      nsAutoString tmpMessage;
-      nssComponent->GetPIPNSSBundleString(stringID, tmpMessage);
+  nsresult rv;
+  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
+  if (NS_SUCCEEDED(rv)) {
+    nsAutoString tmpMessage;
+    nssComponent->GetPIPNSSBundleString(stringID, tmpMessage);
 
-      nsCOMPtr<nsIPrompt> prompt (do_GetInterface(my_ctx));
-      if (!prompt)
-        return;
-    
-      prompt->Alert(nullptr, tmpMessage.get());
+    nsCOMPtr<nsIPrompt> prompt (do_GetInterface(my_ctx));
+    if (!prompt) {
+      return;
     }
+
+    prompt->Alert(nullptr, tmpMessage.get());
   }
 }
 
 
 NS_IMETHODIMP 
 nsNSSCertificateDB::ImportUserCertificate(uint8_t *data, uint32_t length, nsIInterfaceRequestor *ctx)
 {
   if (!NS_IsMainThread()) {
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1266,44 +1266,37 @@ nsNSSComponent::RandomUpdate(void* entro
   PK11_RandomUpdate(entropy, bufLen);
   return NS_OK;
 }
 
 static const char* const PROFILE_CHANGE_NET_TEARDOWN_TOPIC
   = "profile-change-net-teardown";
 static const char* const PROFILE_CHANGE_NET_RESTORE_TOPIC
   = "profile-change-net-restore";
-static const char* const PROFILE_CHANGE_TEARDOWN_TOPIC
-  = "profile-change-teardown";
 static const char* const PROFILE_BEFORE_CHANGE_TOPIC = "profile-before-change";
 static const char* const PROFILE_DO_CHANGE_TOPIC = "profile-do-change";
 
 NS_IMETHODIMP
 nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
                         const char16_t* someData)
 {
-  if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_TEARDOWN_TOPIC) == 0) {
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("in PSM code, receiving change-teardown\n"));
-    DoProfileChangeTeardown(aSubject);
-  }
-  else if (nsCRT::strcmp(aTopic, PROFILE_BEFORE_CHANGE_TOPIC) == 0) {
+  if (nsCRT::strcmp(aTopic, PROFILE_BEFORE_CHANGE_TOPIC) == 0) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("receiving profile change topic\n"));
     DoProfileBeforeChange(aSubject);
   }
   else if (nsCRT::strcmp(aTopic, PROFILE_DO_CHANGE_TOPIC) == 0) {
     if (someData && NS_LITERAL_STRING("startup").Equals(someData)) {
       // The application is initializing against a known profile directory for
       // the first time during process execution.
       // However, earlier code execution might have already triggered NSS init.
       // We must ensure that NSS gets shut down prior to any attempt to init
       // it again. We use the same cleanup functionality used when switching
       // profiles. The order of function calls must correspond to the order
       // of notifications sent by Profile Manager (nsProfile).
       DoProfileChangeNetTeardown();
-      DoProfileChangeTeardown(aSubject);
       DoProfileBeforeChange(aSubject);
       DoProfileChangeNetRestore();
     }
 
     bool needsInit = true;
 
     {
       MutexAutoLock lock(mutex);
@@ -1416,23 +1409,17 @@ nsNSSComponent::GetNewPrompter(nsIPrompt
 }
 
 /*static*/ nsresult
 nsNSSComponent::ShowAlertWithConstructedString(const nsString& message)
 {
   nsCOMPtr<nsIPrompt> prompter;
   nsresult rv = GetNewPrompter(getter_AddRefs(prompter));
   if (prompter) {
-    nsPSMUITracker tracker;
-    if (tracker.isUIForbidden()) {
-      NS_WARNING("Suppressing alert because PSM UI is forbidden");
-      rv = NS_ERROR_UNEXPECTED;
-    } else {
-      rv = prompter->Alert(nullptr, message.get());
-    }
+    rv = prompter->Alert(nullptr, message.get());
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsNSSComponent::ShowAlertFromStringBundle(const char* messageID)
 {
   nsString message;
@@ -1477,17 +1464,16 @@ nsNSSComponent::RegisterObservers()
     // Once we are loaded, don't allow being removed from memory.
     // This makes sense, as initializing NSS is expensive.
 
     // By using false for parameter ownsWeak in AddObserver,
     // we make sure that we won't get unloaded until the application shuts down.
 
     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 
-    observerService->AddObserver(this, PROFILE_CHANGE_TEARDOWN_TOPIC, false);
     observerService->AddObserver(this, PROFILE_BEFORE_CHANGE_TOPIC, false);
     observerService->AddObserver(this, PROFILE_DO_CHANGE_TOPIC, false);
     observerService->AddObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC, false);
     observerService->AddObserver(this, PROFILE_CHANGE_NET_RESTORE_TOPIC, false);
   }
   return NS_OK;
 }
 
@@ -1500,17 +1486,16 @@ nsNSSComponent::DeregisterObservers()
   nsCOMPtr<nsIObserverService> observerService(do_GetService("@mozilla.org/observer-service;1"));
   NS_ASSERTION(observerService, "could not get observer service");
   if (observerService) {
     mObserversRegistered = false;
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent: removing observers\n"));
 
     observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
 
-    observerService->RemoveObserver(this, PROFILE_CHANGE_TEARDOWN_TOPIC);
     observerService->RemoveObserver(this, PROFILE_BEFORE_CHANGE_TOPIC);
     observerService->RemoveObserver(this, PROFILE_DO_CHANGE_TOPIC);
     observerService->RemoveObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC);
     observerService->RemoveObserver(this, PROFILE_CHANGE_NET_RESTORE_TOPIC);
   }
   return NS_OK;
 }
 
@@ -1518,22 +1503,16 @@ void
 nsNSSComponent::DoProfileChangeNetTeardown()
 {
   if (mCertVerificationThread)
     mCertVerificationThread->requestExit();
   mIsNetworkDown = true;
 }
 
 void
-nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
-{
-  mShutdownObjectList->ifPossibleDisallowUI();
-}
-
-void
 nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject)
 {
   NS_ASSERTION(mIsNetworkDown, "nsNSSComponent relies on profile manager to wait for synchronous shutdown of all network activity");
 
   bool needsCleanup = true;
 
   {
     MutexAutoLock lock(mutex);
@@ -1544,17 +1523,16 @@ nsNSSComponent::DoProfileBeforeChange(ns
       // multiple times.
       needsCleanup = false;
     }
   }
 
   if (needsCleanup) {
     ShutdownNSS();
   }
-  mShutdownObjectList->allowUI();
 }
 
 void
 nsNSSComponent::DoProfileChangeNetRestore()
 {
   // XXX this doesn't work well, since nothing expects null pointers
   deleteBackgroundThreads();
   createBackgroundThreads();
@@ -1644,43 +1622,34 @@ getNSSDialogs(void** _result, REFNSIID a
   }
 
   rv = svc->QueryInterface(aIID, _result);
 
   return rv;
 }
 
 nsresult
-setPassword(PK11SlotInfo* slot, nsIInterfaceRequestor* ctx)
+setPassword(PK11SlotInfo* slot, nsIInterfaceRequestor* ctx,
+            nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
-  nsNSSShutDownPreventionLock locker;
   nsresult rv = NS_OK;
 
   if (PK11_NeedUserInit(slot)) {
     nsITokenPasswordDialogs* dialogs;
     bool canceled;
     NS_ConvertUTF8toUTF16 tokenName(PK11_GetTokenName(slot));
 
     rv = getNSSDialogs((void**)&dialogs,
                        NS_GET_IID(nsITokenPasswordDialogs),
                        NS_TOKENPASSWORDSDIALOG_CONTRACTID);
 
     if (NS_FAILED(rv)) goto loser;
 
-    {
-      nsPSMUITracker tracker;
-      if (tracker.isUIForbidden()) {
-        rv = NS_ERROR_NOT_AVAILABLE;
-      }
-      else {
-        rv = dialogs->SetPassword(ctx,
-                                  tokenName.get(),
-                                  &canceled);
-      }
-    }
+    rv = dialogs->SetPassword(ctx, tokenName.get(), &canceled);
+
     NS_RELEASE(dialogs);
     if (NS_FAILED(rv)) goto loser;
 
     if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
   }
  loser:
   return rv;
 }
--- a/security/manager/ssl/nsNSSComponent.h
+++ b/security/manager/ssl/nsNSSComponent.h
@@ -179,17 +179,16 @@ private:
   nsresult InitializePIPNSSBundle();
   nsresult ConfigureInternalPKCS11Token();
   nsresult RegisterObservers();
   nsresult DeregisterObservers();
 
   // Methods that we use to handle the profile change notifications (and to
   // synthesize a full profile change when we're just doing a profile startup):
   void DoProfileChangeNetTeardown();
-  void DoProfileChangeTeardown(nsISupports* aSubject);
   void DoProfileBeforeChange(nsISupports* aSubject);
   void DoProfileChangeNetRestore();
 
   Mutex mutex;
 
   nsCOMPtr<nsIStringBundle> mPIPNSSBundle;
   nsCOMPtr<nsIStringBundle> mNSSErrorsBundle;
   bool mNSSInitialized;
--- a/security/manager/ssl/nsNSSHelper.h
+++ b/security/manager/ssl/nsNSSHelper.h
@@ -4,16 +4,17 @@
  * 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 NSS_HELPER_
 #define NSS_HELPER_
 
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
+#include "nsNSSShutDown.h"
 #include "pk11func.h"
 
 //
 // Implementation of an nsIInterfaceRequestor for use
 // as context for NSS calls
 //
 class PipUIContext : public nsIInterfaceRequestor
 {
@@ -46,12 +47,12 @@ pip_ucs2_ascii_conversion_fn(PRBool toUn
                              unsigned int *outBufLen,
                              PRBool swapBytes);
 }
 
 //
 // A function that sets the password on an unitialized slot.
 //
 nsresult
-setPassword(PK11SlotInfo *slot, nsIInterfaceRequestor *ctx);
+setPassword(PK11SlotInfo* slot, nsIInterfaceRequestor* ctx,
+            nsNSSShutDownPreventionLock& /*proofOfLock*/);
 
 #endif
-
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -2379,28 +2379,21 @@ ClientAuthDataRunnable::RunOnTargetThrea
         NS_CLIENTAUTHDIALOGS_CONTRACTID);
 
       if (NS_FAILED(rv)) {
         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
         goto loser;
       }
 
-      {
-        nsPSMUITracker tracker;
-        if (tracker.isUIForbidden()) {
-          rv = NS_ERROR_NOT_AVAILABLE;
-        } else {
-          rv = dialogs->ChooseCertificate(mSocketInfo, cn_host_port.get(),
-            org.get(), issuer.get(),
-            (const char16_t**)certNicknameList,
-            (const char16_t**)certDetailsList,
-            CertsToUse, &selectedIndex, &canceled);
-        }
-      }
+      rv = dialogs->ChooseCertificate(mSocketInfo, cn_host_port.get(),
+                                      org.get(), issuer.get(),
+                                      (const char16_t**)certNicknameList,
+                                      (const char16_t**)certDetailsList,
+                                      CertsToUse, &selectedIndex, &canceled);
 
       NS_RELEASE(dialogs);
       NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
       NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
 
       if (NS_FAILED(rv)) goto loser;
 
       // even if the user has canceled, we want to remember that, to avoid repeating prompts
--- a/security/manager/ssl/nsNSSShutDown.cpp
+++ b/security/manager/ssl/nsNSSShutDown.cpp
@@ -107,27 +107,16 @@ nsresult nsNSSShutDownList::doPK11Logout
     if (pklco) {
       pklco->logout();
     }
   }
 
   return NS_OK;
 }
 
-bool nsNSSShutDownList::ifPossibleDisallowUI()
-{
-  bool isNowDisallowed = mActivityState.ifPossibleDisallowUI();
-  return isNowDisallowed;
-}
-
-void nsNSSShutDownList::allowUI()
-{
-  mActivityState.allowUI();
-}
-
 nsresult nsNSSShutDownList::evaporateAllNSSResources()
 {
   if (PR_SUCCESS != mActivityState.restrictActivityToCurrentThread()) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed to restrict activity to current thread\n"));
     return NS_ERROR_FAILURE;
   }
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("now evaporating NSS resources\n"));
@@ -164,18 +153,16 @@ nsNSSShutDownList *nsNSSShutDownList::co
   return singleton;
 }
 
 nsNSSActivityState::nsNSSActivityState()
 :mNSSActivityStateLock("nsNSSActivityState.mNSSActivityStateLock"), 
  mNSSActivityChanged(mNSSActivityStateLock,
                      "nsNSSActivityState.mNSSActivityStateLock"),
  mNSSActivityCounter(0),
- mBlockingUICounter(0),
- mIsUIForbidden(false),
  mNSSRestrictedThread(nullptr)
 {
 }
 
 nsNSSActivityState::~nsNSSActivityState()
 {
 }
 
@@ -194,97 +181,34 @@ void nsNSSActivityState::leave()
 {
   MutexAutoLock lock(mNSSActivityStateLock);
 
   --mNSSActivityCounter;
 
   mNSSActivityChanged.NotifyAll();
 }
 
-void nsNSSActivityState::enterBlockingUIState()
-{
-  MutexAutoLock lock(mNSSActivityStateLock);
-
-  ++mBlockingUICounter;
-}
-
-void nsNSSActivityState::leaveBlockingUIState()
+PRStatus nsNSSActivityState::restrictActivityToCurrentThread()
 {
   MutexAutoLock lock(mNSSActivityStateLock);
 
-  --mBlockingUICounter;
-}
-
-bool nsNSSActivityState::isBlockingUIActive()
-{
-  MutexAutoLock lock(mNSSActivityStateLock);
-  return (mBlockingUICounter > 0);
-}
-
-bool nsNSSActivityState::isUIForbidden()
-{
-  MutexAutoLock lock(mNSSActivityStateLock);
-  return mIsUIForbidden;
-}
-
-bool nsNSSActivityState::ifPossibleDisallowUI()
-{
-  bool retval = false;
-  MutexAutoLock lock(mNSSActivityStateLock);
-
-  // Checking and disallowing the UI must be done atomically.
-
-  if (!mBlockingUICounter) {
-    // No UI is currently shown, we are able to evaporate.
-    retval = true;
-    // Remember to disallow UI.
-    // To clear the "forbidden" state, call restrictActivityToCurrentThread()
-    // and releaseCurrentThreadActivityRestriction().
-    mIsUIForbidden = true;
-  }
-  return retval;
-}
-
-void nsNSSActivityState::allowUI()
-{
-  MutexAutoLock lock(mNSSActivityStateLock);
-
-  mIsUIForbidden = false;
-}
-
-PRStatus nsNSSActivityState::restrictActivityToCurrentThread()
-{
-  PRStatus retval = PR_FAILURE;
-  MutexAutoLock lock(mNSSActivityStateLock);
-  
-  if (!mBlockingUICounter) {
-    while (0 < mNSSActivityCounter && !mBlockingUICounter) {
-      mNSSActivityChanged.Wait(PR_TicksPerSecond());
-    }
-      
-    if (mBlockingUICounter) {
-      // This should never happen.
-      // If we arrive here, our logic is broken.
-      PR_ASSERT(0);
-    }
-    else {
-      mNSSRestrictedThread = PR_GetCurrentThread();
-      retval = PR_SUCCESS;
-    }
+  while (mNSSActivityCounter > 0) {
+    mNSSActivityChanged.Wait(PR_TicksPerSecond());
   }
 
-  return retval;
+  mNSSRestrictedThread = PR_GetCurrentThread();
+
+  return PR_SUCCESS;
 }
 
 void nsNSSActivityState::releaseCurrentThreadActivityRestriction()
 {
   MutexAutoLock lock(mNSSActivityStateLock);
 
   mNSSRestrictedThread = nullptr;
-  mIsUIForbidden = false;
 
   mNSSActivityChanged.NotifyAll();
 }
 
 nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock()
 {
   nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
   if (!state)
@@ -296,35 +220,8 @@ nsNSSShutDownPreventionLock::nsNSSShutDo
 nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock()
 {
   nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
   if (!state)
     return;
   
   state->leave();
 }
-
-nsPSMUITracker::nsPSMUITracker()
-{
-  nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
-  if (!state)
-    return;
-  
-  state->enterBlockingUIState();
-}
-
-nsPSMUITracker::~nsPSMUITracker()
-{
-  nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
-  if (!state)
-    return;
-  
-  state->leaveBlockingUIState();
-}
-
-bool nsPSMUITracker::isUIForbidden()
-{
-  nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
-  if (!state)
-    return false;
-
-  return state->isUIForbidden();
-}
--- a/security/manager/ssl/nsNSSShutDown.h
+++ b/security/manager/ssl/nsNSSShutDown.h
@@ -20,40 +20,18 @@ class nsNSSActivityState
 public:
   nsNSSActivityState();
   ~nsNSSActivityState();
 
   // Call enter/leave when PSM enters a scope during which
   // shutting down NSS is prohibited.
   void enter();
   void leave();
-  
-  // Call enter/leave when PSM is about to show a UI
-  // while still holding resources.
-  void enterBlockingUIState();
-  void leaveBlockingUIState();
-  
-  // Is the activity aware of any blocking PSM UI currently shown?
-  bool isBlockingUIActive();
-
-  // Is it forbidden to bring up an UI while holding resources?
-  bool isUIForbidden();
-  
-  // Check whether setting the current thread restriction is possible. If it
-  // is possible, the state tracking will have ensured that we will stay in
-  // this state. As of writing, this includes forbidding PSM UI.
-  bool ifPossibleDisallowUI();
-
-  // Notify the state tracking that going to the restricted state is
-  // no longer planned.
-  // As of writing, this includes clearing the "PSM UI forbidden" flag.
-  void allowUI();
-
-  // If currently no UI is shown, wait for all activity to stop,
-  // and block any other thread on entering relevant PSM code.
+  // 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;
@@ -61,46 +39,29 @@ private:
   // 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;
 
-  // The number of scopes holding resources while blocked
-  // showing an UI.
-  int mBlockingUICounter;
-
-  // Whether bringing up UI is currently forbidden
-  bool mIsUIForbidden;
-
   // 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();
 };
 
-// Helper class that automatically enters/leaves the global UI tracking
-class nsPSMUITracker
-{
-public:
-  nsPSMUITracker();
-  ~nsPSMUITracker();
-  
-  bool isUIForbidden();
-};
-
 // Singleton, used by nsNSSComponent to track the list of PSM objects,
 // which hold NSS resources and support the "early cleanup mechanism".
 class nsNSSShutDownList
 {
 public:
   ~nsNSSShutDownList();
 
   static nsNSSShutDownList *construct();
@@ -109,23 +70,16 @@ public:
   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);
 
-  // If possible to do "early cleanup" at the current time, remember that we want to
-  // do it, and disallow actions that would change the possibility.
-  bool ifPossibleDisallowUI();
-
-  // Notify that it is no longer planned to do the "early cleanup".
-  void allowUI();
-  
   // Do the "early cleanup", if possible.
   nsresult evaporateAllNSSResources();
 
   // PSM has been asked to log out of a token.
   // Notify all registered instances that want to react to that event.
   nsresult doPK11Logout();
   
   static nsNSSActivityState *getActivityState()
--- a/security/manager/ssl/nsPK11TokenDB.cpp
+++ b/security/manager/ssl/nsPK11TokenDB.cpp
@@ -192,17 +192,17 @@ nsPK11Token::Login(bool force)
   SECStatus srv;
   bool test;
   rv = this->NeedsLogin(&test);
   if (NS_FAILED(rv)) return rv;
   if (test && force) {
     rv = this->LogoutSimple();
     if (NS_FAILED(rv)) return rv;
   }
-  rv = setPassword(mSlot, mUIContext);
+  rv = setPassword(mSlot, mUIContext, locker);
   if (NS_FAILED(rv)) return rv;
   srv = PK11_Authenticate(mSlot, true, mUIContext);
   return (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP nsPK11Token::LogoutSimple()
 {
   nsNSSShutDownPreventionLock locker;
--- a/security/manager/ssl/nsPKCS12Blob.cpp
+++ b/security/manager/ssl/nsPKCS12Blob.cpp
@@ -56,31 +56,41 @@ nsPKCS12Blob::nsPKCS12Blob():mCertArray(
   mUIContext = new PipUIContext();
 }
 
 // destructor
 nsPKCS12Blob::~nsPKCS12Blob()
 {
   delete mDigestIterator;
   delete mDigest;
+
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+
+  shutdown(calledFromObject);
 }
 
 // nsPKCS12Blob::SetToken
 //
 // Set the token to use for import/export
 nsresult
 nsPKCS12Blob::SetToken(nsIPK11Token *token)
 {
  nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+  return NS_ERROR_NOT_AVAILABLE;
+ }
  nsresult rv = NS_OK;
  if (token) {
    mToken = token;
  } else {
    PK11SlotInfo *slot;
-   rv = GetSlotWithMechanism(CKM_RSA_PKCS, mUIContext,&slot);
+   rv = GetSlotWithMechanism(CKM_RSA_PKCS, mUIContext, &slot, locker);
    if (NS_FAILED(rv)) {
       mToken = 0;  
    } else {
      mToken = new nsPK11Token(slot);
      PK11_FreeSlot(slot);
    }
  }
  mTokenSet = true;
@@ -437,25 +447,17 @@ nsPKCS12Blob::newPKCS12FilePassword(SECI
   nsresult rv = NS_OK;
   nsAutoString password;
   nsCOMPtr<nsICertificateDialogs> certDialogs;
   rv = ::getNSSDialogs(getter_AddRefs(certDialogs), 
                        NS_GET_IID(nsICertificateDialogs),
                        NS_CERTIFICATEDIALOGS_CONTRACTID);
   if (NS_FAILED(rv)) return rv;
   bool pressedOK;
-  {
-    nsPSMUITracker tracker;
-    if (tracker.isUIForbidden()) {
-      rv = NS_ERROR_NOT_AVAILABLE;
-    }
-    else {
-      rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK);
-    }
-  }
+  rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK);
   if (NS_FAILED(rv) || !pressedOK) return rv;
   unicodeToItem(password.get(), unicodePw);
   return NS_OK;
 }
 
 // getPKCS12FilePassword
 //
 // Launch a dialog requesting the user for the password to a PKCS#12 file.
@@ -466,25 +468,17 @@ nsPKCS12Blob::getPKCS12FilePassword(SECI
   nsresult rv = NS_OK;
   nsAutoString password;
   nsCOMPtr<nsICertificateDialogs> certDialogs;
   rv = ::getNSSDialogs(getter_AddRefs(certDialogs), 
                        NS_GET_IID(nsICertificateDialogs),
                        NS_CERTIFICATEDIALOGS_CONTRACTID);
   if (NS_FAILED(rv)) return rv;
   bool pressedOK;
-  {
-    nsPSMUITracker tracker;
-    if (tracker.isUIForbidden()) {
-      rv = NS_ERROR_NOT_AVAILABLE;
-    }
-    else {
-      rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK);
-    }
-  }
+  rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK);
   if (NS_FAILED(rv) || !pressedOK) return rv;
   unicodeToItem(password.get(), unicodePw);
   return NS_OK;
 }
 
 // inputToDecoder
 //
 // Given a decoder, read bytes from file and input them to the decoder.
--- a/security/manager/ssl/nsPKCS12Blob.h
+++ b/security/manager/ssl/nsPKCS12Blob.h
@@ -21,22 +21,25 @@
 
 class nsIX509Cert;
 
 //
 // nsPKCS12Blob
 //
 // Class for importing/exporting PKCS#12 blobs
 //
-class nsPKCS12Blob
+class nsPKCS12Blob : public nsNSSShutDownObject
 {
 public:
   nsPKCS12Blob();
   virtual ~nsPKCS12Blob();
 
+  // Nothing to release.
+  virtual void virtualDestroyNSSReference() override {}
+
   // Set the token to use (default is internal)
   nsresult SetToken(nsIPK11Token *token);
 
   // PKCS#12 Import
   nsresult ImportFromFile(nsIFile *file);
 
   // PKCS#12 Export
   nsresult ExportToFile(nsIFile *file, nsIX509Cert **certs, int numCerts);
--- a/security/manager/ssl/nsSDR.cpp
+++ b/security/manager/ssl/nsSDR.cpp
@@ -40,35 +40,46 @@ NS_IMPL_ISUPPORTS(nsSecretDecoderRing, n
 nsSecretDecoderRing::nsSecretDecoderRing()
 {
   // initialize superclass
 }
 
 // nsSecretDecoderRing destructor
 nsSecretDecoderRing::~nsSecretDecoderRing()
 {
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+
+  shutdown(calledFromObject);
 }
 
-NS_IMETHODIMP nsSecretDecoderRing::
-Encrypt(unsigned char * data, int32_t dataLen, unsigned char * *result, int32_t *_retval)
+NS_IMETHODIMP
+nsSecretDecoderRing::Encrypt(unsigned char* data, int32_t dataLen,
+                             unsigned char** result, int32_t* _retval)
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsresult rv = NS_OK;
   ScopedPK11SlotInfo slot;
   SECItem keyid;
   SECItem request;
   SECItem reply;
   SECStatus s;
   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
 
   slot = PK11_GetInternalKeySlot();
   if (!slot) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
 
   /* Make sure token is initialized. */
-  rv = setPassword(slot, ctx);
+  rv = setPassword(slot, ctx, locker);
   if (NS_FAILED(rv))
     goto loser;
 
   /* Force authentication */
   s = PK11_Authenticate(slot, true, ctx);
   if (s != SECSuccess) { rv = NS_ERROR_FAILURE; goto loser; }
 
   /* Use default key id */
@@ -185,20 +196,24 @@ DecryptString(const char *crypt, char **
 
 loser:
   if (decrypted) PORT_Free(decrypted);
   if (decoded) PR_DELETE(decoded);
 
   return rv;
 }
 
-NS_IMETHODIMP nsSecretDecoderRing::
-ChangePassword()
+NS_IMETHODIMP
+nsSecretDecoderRing::ChangePassword()
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsresult rv;
   ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
   if (!slot) return NS_ERROR_NOT_AVAILABLE;
 
   /* Convert UTF8 token name to UCS2 */
   NS_ConvertUTF8toUTF16 tokenName(PK11_GetTokenName(slot));
 
   /* Get the set password dialog handler imlementation */
@@ -206,27 +221,17 @@ ChangePassword()
 
   rv = getNSSDialogs(getter_AddRefs(dialogs),
                      NS_GET_IID(nsITokenPasswordDialogs),
                      NS_TOKENPASSWORDSDIALOG_CONTRACTID);
   if (NS_FAILED(rv)) return rv;
 
   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   bool canceled;
-
-  {
-    nsPSMUITracker tracker;
-    if (tracker.isUIForbidden()) {
-      rv = NS_ERROR_NOT_AVAILABLE;
-    }
-    else {
-      rv = dialogs->SetPassword(ctx, tokenName.get(), &canceled);
-    }
-  }
-
+  rv = dialogs->SetPassword(ctx, tokenName.get(), &canceled);
   /* canceled is ignored */
 
   return rv;
 }
 
 NS_IMETHODIMP nsSecretDecoderRing::
 Logout()
 {
--- a/security/manager/ssl/nsSDR.h
+++ b/security/manager/ssl/nsSDR.h
@@ -25,34 +25,36 @@
 
 // ===============================================
 // nsSecretDecoderRing - implementation of nsISecretDecoderRing
 // ===============================================
 
 #define NS_SDR_CID \
   { 0x0c4f1ddc, 0x1dd2, 0x11b2, { 0x9d, 0x95, 0xf2, 0xfd, 0xf1, 0x13, 0x04, 0x4b } }
 
-class nsSecretDecoderRing
-: public nsISecretDecoderRing,
-  public nsISecretDecoderRingConfig
+class nsSecretDecoderRing : public nsISecretDecoderRing
+                          , public nsISecretDecoderRingConfig
+                          , public nsNSSShutDownObject
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISECRETDECODERRING
   NS_DECL_NSISECRETDECODERRINGCONFIG
 
   nsSecretDecoderRing();
 
+  // Nothing to release.
+  virtual void virtualDestroyNSSReference() override {}
+
 protected:
   virtual ~nsSecretDecoderRing();
 
 private:
 
   /**
    * encode - encodes binary into BASE64 string.
    * decode - decode BASE64 string into binary.
    */
   nsresult encode(const unsigned char *data, int32_t dataLen, char **_retval);
   nsresult decode(const char *data, unsigned char **result, int32_t * _retval);
-
 };
 
 #endif /* _NSSDR_H_ */