bug 1492188 - avoid using the directory service off the main thread when loading loadable roots r=jcj
authorDana Keeler <dkeeler@mozilla.com>
Thu, 27 Sep 2018 22:45:56 +0000
changeset 438724 a6a603cfb2677eb9a3531e86968418321b149f2e
parent 438723 57a2d6865e170770853a5c42417d038b9cec3414
child 438725 aff180170b3d1326957a70f84b5f0c86a1b0f6b3
push id70091
push userdkeeler@mozilla.com
push dateFri, 28 Sep 2018 17:12:36 +0000
treeherderautoland@a6a603cfb267 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjcj
bugs1492188
milestone64.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 1492188 - avoid using the directory service off the main thread when loading loadable roots r=jcj Differential Revision: https://phabricator.services.mozilla.com/D6692
security/manager/ssl/nsNSSComponent.cpp
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -772,37 +772,40 @@ nsNSSComponent::GetEnterpriseRoots(nsIX5
   }
   enterpriseRootsCertList.forget(enterpriseRoots);
   return NS_OK;
 }
 
 class LoadLoadableRootsTask final : public Runnable
 {
 public:
-  explicit LoadLoadableRootsTask(nsNSSComponent* nssComponent,
-                                 bool importEnterpriseRoots,
-                                 uint32_t familySafetyMode)
+  LoadLoadableRootsTask(nsNSSComponent* nssComponent,
+                        bool importEnterpriseRoots,
+                        uint32_t familySafetyMode,
+                        Vector<nsCString>&& possibleLoadableRootsLocations)
     : Runnable("LoadLoadableRootsTask")
     , mNSSComponent(nssComponent)
     , mImportEnterpriseRoots(importEnterpriseRoots)
     , mFamilySafetyMode(familySafetyMode)
+    , mPossibleLoadableRootsLocations(std::move(possibleLoadableRootsLocations))
   {
     MOZ_ASSERT(nssComponent);
   }
 
   ~LoadLoadableRootsTask() = default;
 
   nsresult Dispatch();
 
 private:
   NS_IMETHOD Run() override;
   nsresult LoadLoadableRoots();
   RefPtr<nsNSSComponent> mNSSComponent;
   bool mImportEnterpriseRoots;
   uint32_t mFamilySafetyMode;
+  Vector<nsCString> mPossibleLoadableRootsLocations;
   nsCOMPtr<nsIThread> mThread;
 };
 
 nsresult
 LoadLoadableRootsTask::Dispatch()
 {
   // Can't add 'this' as the event to run, since mThread may not be set yet
   nsresult rv = NS_NewNamedThread("LoadRoots", getter_AddRefs(mThread),
@@ -983,16 +986,18 @@ nsNSSComponent::CheckForSmartCardChanges
   return NS_OK;
 }
 
 // Returns by reference the path to the directory containing the file that has
 // been loaded as MOZ_DLL_PREFIX nss3 MOZ_DLL_SUFFIX.
 static nsresult
 GetNSS3Directory(nsCString& result)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   UniquePRString nss3Path(
     PR_GetLibraryFilePathname(MOZ_DLL_PREFIX "nss3" MOZ_DLL_SUFFIX,
                               reinterpret_cast<PRFuncPtr>(NSS_Initialize)));
   if (!nss3Path) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nss not loaded?"));
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsIFile> nss3File(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
@@ -1027,16 +1032,18 @@ GetNSS3Directory(nsCString& result)
 #endif
 }
 
 // Returns by reference the path to the desired directory, based on the current
 // settings in the directory service.
 static nsresult
 GetDirectoryPath(const char* directoryKey, nsCString& result)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsIProperties> directoryService(
     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
   if (!directoryService) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not get directory service"));
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsIFile> directory;
   nsresult rv = directoryService->Get(directoryKey, NS_GET_IID(nsIFile),
@@ -1055,68 +1062,79 @@ GetDirectoryPath(const char* directoryKe
     return rv;
   }
   return directoryWin->GetNativeCanonicalPath(result);
 #else
   return directory->GetNativePath(result);
 #endif
 }
 
-
-nsresult
-LoadLoadableRootsTask::LoadLoadableRoots()
+// The loadable roots library is probably in the same directory we loaded the
+// NSS shared library from, but in some cases it may be elsewhere. This function
+// enumerates and returns the possible locations as nsCStrings.
+static nsresult
+ListPossibleLoadableRootsLocations(
+  Vector<nsCString>& possibleLoadableRootsLocations)
 {
-  // Find the best Roots module for our purposes.
-  // Prefer the application's installation directory,
-  // but also ensure the library is at least the version we expect.
-  Vector<nsCString> possibleCKBILocations;
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!NS_IsMainThread()) {
+    return NS_ERROR_NOT_SAME_THREAD;
+  }
+
   // First try in the directory where we've already loaded
   // MOZ_DLL_PREFIX nss3 MOZ_DLL_SUFFIX, since that's likely to be correct.
   nsAutoCString nss3Dir;
   nsresult rv = GetNSS3Directory(nss3Dir);
   if (NS_SUCCEEDED(rv)) {
-    if (!possibleCKBILocations.append(std::move(nss3Dir))) {
+    if (!possibleLoadableRootsLocations.append(std::move(nss3Dir))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   } else {
     // For some reason this fails on android. In any case, we should try with
     // the other potential locations we have.
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("could not determine where nss was loaded from"));
   }
   nsAutoCString currentProcessDir;
   rv = GetDirectoryPath(NS_XPCOM_CURRENT_PROCESS_DIR, currentProcessDir);
   if (NS_SUCCEEDED(rv)) {
-    if (!possibleCKBILocations.append(std::move(currentProcessDir))) {
+    if (!possibleLoadableRootsLocations.append(std::move(currentProcessDir))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   } else {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("could not get current process directory"));
   }
   nsAutoCString greDir;
   rv = GetDirectoryPath(NS_GRE_DIR, greDir);
   if (NS_SUCCEEDED(rv)) {
-    if (!possibleCKBILocations.append(std::move(greDir))) {
+    if (!possibleLoadableRootsLocations.append(std::move(greDir))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   } else {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not get gre directory"));
   }
   // As a last resort, this will cause the library loading code to use the OS'
   // default library search path.
   nsAutoCString emptyString;
-  if (!possibleCKBILocations.append(std::move(emptyString))) {
+  if (!possibleLoadableRootsLocations.append(std::move(emptyString))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  for (const auto& possibleCKBILocation : possibleCKBILocations) {
-    if (mozilla::psm::LoadLoadableRoots(possibleCKBILocation)) {
-      MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("loaded CKBI from %s",
-                                            possibleCKBILocation.get()));
+  return NS_OK;
+}
+
+nsresult
+LoadLoadableRootsTask::LoadLoadableRoots()
+{
+  for (const auto& possibleLocation : mPossibleLoadableRootsLocations) {
+    if (mozilla::psm::LoadLoadableRoots(possibleLocation)) {
+      MOZ_LOG(gPIPNSSLog,
+              LogLevel::Debug,
+              ("loaded CKBI from %s", possibleLocation.get()));
       return NS_OK;
     }
   }
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not load loadable roots"));
   return NS_ERROR_FAILURE;
 }
 
 // Table of pref names and SSL cipher ID
@@ -1482,16 +1500,18 @@ nsNSSComponent::setEnabledTLSVersions()
 
 #if defined(XP_WIN) || (defined(XP_LINUX) && !defined(ANDROID))
 // If the profile directory is on a networked drive, we want to set the
 // environment variable NSS_SDB_USE_CACHE to yes (as long as it hasn't been set
 // before).
 static void
 SetNSSDatabaseCacheModeAsAppropriate()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsIFile> profileFile;
   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                        getter_AddRefs(profileFile));
   if (NS_FAILED(rv)) {
     // We're probably running without a profile directory, so this is
     // irrelevant.
     return;
   }
@@ -1959,18 +1979,26 @@ nsNSSComponent::InitializeNSS()
     // Set dynamic options from prefs. This has to run after
     // SSL_ConfigServerSessionIDCache.
     setValidationOptions(true, lock);
 
     bool importEnterpriseRoots = Preferences::GetBool(kEnterpriseRootModePref,
                                                       false);
     uint32_t familySafetyMode = Preferences::GetUint(kFamilySafetyModePref,
                                                      kFamilySafetyModeDefault);
+    Vector<nsCString> possibleLoadableRootsLocations;
+    rv = ListPossibleLoadableRootsLocations(possibleLoadableRootsLocations);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
     RefPtr<LoadLoadableRootsTask> loadLoadableRootsTask(
-      new LoadLoadableRootsTask(this, importEnterpriseRoots, familySafetyMode));
+      new LoadLoadableRootsTask(this,
+                                importEnterpriseRoots,
+                                familySafetyMode,
+                                std::move(possibleLoadableRootsLocations)));
     rv = loadLoadableRootsTask->Dispatch();
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     mLoadLoadableRootsTaskDispatched = true;
     return NS_OK;
   }