move init to main thread so NS_GetSpecialDirectory works draft
authorMark Goodwin <mgoodwin@mozilla.com>
Mon, 05 Jan 2015 12:49:33 +0000
changeset 345620 b250fee37b9ad2ab8bf479ededabefaeef6911db
parent 345619 4eed64c932d69ed816e38528b7fbc635da9c5820
child 345621 91cbd2cc8131ec78c8e312f79ac2721b25a551a8
child 345645 ef70d0d355ba94c8bc0e521b69c2215bf210d823
push id47400
push usermgoodwin@mozilla.com
push dateMon, 05 Jan 2015 12:57:00 +0000
treeherdertry@91cbd2cc8131 [default view] [failures only]
milestone37.0a1
move init to main thread so NS_GetSpecialDirectory works * * * make the IO lazy so only the directory service stuff happens on main thread
security/manager/boot/src/CertBlocklist.cpp
security/manager/boot/src/CertBlocklist.h
security/manager/ssl/src/nsNSSComponent.cpp
--- a/security/manager/boot/src/CertBlocklist.cpp
+++ b/security/manager/boot/src/CertBlocklist.cpp
@@ -96,118 +96,140 @@ CertBlocklistItem::Hash() const
 
 CertBlocklist::CertBlocklist()
   : mMutex("CertBlocklist::mMutex")
   , mModified(false)
 {
   if (!gCertBlockPRLog) {
     gCertBlockPRLog = PR_NewLogModule("CertBlock");
   }
+  mBackingIsInitialized = false;
 }
 
 CertBlocklist::~CertBlocklist()
 {
 }
 
 nsresult
 CertBlocklist::Init()
 {
-  mozilla::MutexAutoLock lock(mMutex);
   PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG, ("CertBlocklist::Init"));
 
-  // Load the revocations file into the cert blocklist
+  // Init must be on main thread for getting the profile directory
+  if (!NS_IsMainThread()) {
+    PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
+           ("CertBlocklist::Init - called off main thread"));
+    return NS_ERROR_NOT_SAME_THREAD;
+  }
+
+  // Get the profile directory
   PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
          ("CertBlocklist::Init - not initialized; initializing"));
   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                        getter_AddRefs(mBackingFile));
   if (NS_FAILED(rv) || !mBackingFile) {
     PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
            ("CertBlocklist::Init - couldn't get profile dir"));
-    return NS_OK;
   }
-  rv = mBackingFile->Append(NS_LITERAL_STRING("revocations.txt"));
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+  return NS_OK;
+}
 
-  nsAutoCString path;
-  rv = mBackingFile->GetNativePath(path);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
+nsresult
+CertBlocklist::EnsureBackingFileInitialized(mozilla::MutexAutoLock& lock)
+{
   PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
-         ("CertBlocklist::Init certList path: %s", path.get()));
+      ("CertBlocklist::EnsureBackingFileInitialized"));
+  if (!mBackingIsInitialized) {
+    PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
+        ("CertBlocklist::EnsureBackingFileInitialized - not initialized"));
+    if (mBackingFile) {
+      // Load the revocations file into the cert blocklist
+      nsresult rv = mBackingFile->Append(NS_LITERAL_STRING("revocations.txt"));
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
 
-  bool exists = false;
-  rv = mBackingFile->Exists(&exists);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+      nsAutoCString path;
+      rv = mBackingFile->GetNativePath(path);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+
+      PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
+          ("CertBlocklist::Init certList path: %s", path.get()));
 
-  if (!exists) {
-    PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
-           ("CertBlocklist::Init no revocations file"));
-    return NS_OK;
-  }
+      bool exists = false;
+      rv = mBackingFile->Exists(&exists);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
 
-  nsCOMPtr<nsIFileInputStream> fileStream(
-      do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+      if (!exists) {
+        PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
+            ("CertBlocklist::Init no revocations file"));
+        return NS_OK;
+      }
 
-  rv = fileStream->Init(mBackingFile, -1, -1, false);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+      nsCOMPtr<nsIFileInputStream> fileStream(
+          do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
 
-  nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
-  nsAutoCString line;
-  nsAutoCString issuer;
-  nsAutoCString serial;
-  // read in the revocations file. The file format is as follows: each line
-  // contains a comment, base64 encoded DER for an issuer or base64 encoded DER
-  // for a serial number. Comment lines start with '#', serial number lines, ' '
-  // (a space) and anything else is assumed to be an issuer.
-  bool more = true;
-  do {
-    rv = lineStream->ReadLine(line, &more);
-    if (NS_FAILED(rv)) {
-      break;
-    }
-    // ignore comments and empty lines
-    if (line.IsEmpty() || line.First() == '#') {
-      continue;
-    }
-    if (line.First() != ' ') {
-      issuer = line;
-      continue;
+      rv = fileStream->Init(mBackingFile, -1, -1, false);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+
+      nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
+      nsAutoCString line;
+      nsAutoCString issuer;
+      nsAutoCString serial;
+      // read in the revocations file. The file format is as follows: each line
+      // contains a comment, base64 encoded DER for an issuer or base64 encoded DER
+      // for a serial number. Comment lines start with '#', serial number lines, ' '
+      // (a space) and anything else is assumed to be an issuer.
+      bool more = true;
+      do {
+        rv = lineStream->ReadLine(line, &more);
+        if (NS_FAILED(rv)) {
+          break;
+        }
+        // ignore comments and empty lines
+        if (line.IsEmpty() || line.First() == '#') {
+          continue;
+        }
+        if (line.First() != ' ') {
+          issuer = line;
+          continue;
+        }
+        serial = line;
+        serial.Trim(" ", true, false, false);
+        // serial numbers 'belong' to the last issuer line seen; if no issuer has
+        // been seen, the serial number is ignored
+        if (issuer.IsEmpty() || serial.IsEmpty()) {
+          continue;
+        }
+        PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
+               ("CertBlocklist::Init adding: %s %s",
+                issuer.get(), serial.get()));
+        rv = AddRevokedCertInternal(issuer.get(),
+                                    serial.get(),
+                                    CertOldFromLocalCache,
+                                    lock);
+        if (NS_FAILED(rv)) {
+          // we warn here, rather than abandoning, since we need to
+          // ensure that as many items as possible are read
+          PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
+                 ("CertBlocklist::Init adding revoked cert failed"));
+        }
+      } while (more);
     }
-    serial = line;
-    serial.Trim(" ", true, false, false);
-    // serial numbers 'belong' to the last issuer line seen; if no issuer has
-    // been seen, the serial number is ignored
-    if (issuer.IsEmpty() || serial.IsEmpty()) {
-      continue;
-    }
-    PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
-           ("CertBlocklist::Init adding: %s %s", issuer.get(), serial.get()));
-    rv = AddRevokedCertInternal(issuer.get(),
-                                serial.get(),
-                                CertOldFromLocalCache,
-                                lock);
-    if (NS_FAILED(rv)) {
-      // we warn here, rather than abandoning, since we need to
-      // ensure that as many items as possible are read
-      PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
-             ("CertBlocklist::Init adding revoked cert failed"));
-    }
-  } while (more);
-
+    mBackingIsInitialized = true;
+  }
   return NS_OK;
 }
 
 // void addRevokedCert (in string issuer, in string serialNumber);
 NS_IMETHODIMP
 CertBlocklist::AddRevokedCert(const char* aIssuer,
                               const char* aSerialNumber)
 {
@@ -381,30 +403,38 @@ WriteIssuer(nsCStringHashKey* aHashKey, 
 //
 // Each item is stored on a separate line; each issuer is followed by its
 // revoked serial numbers, indented by one space.
 //
 // lines starting with a # character are ignored
 NS_IMETHODIMP
 CertBlocklist::SaveEntries()
 {
+  PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
+      ("CertBlocklist::SaveEntries - not initialized"));
   mozilla::MutexAutoLock lock(mMutex);
   if (!mModified) {
     return NS_OK;
   }
+
+  nsresult rv = EnsureBackingFileInitialized(lock);
+
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   if (!mBackingFile) {
     // We allow this to succeed with no profile directory for tests
     PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
            ("CertBlocklist::SaveEntries no file in profile to write to"));
     return NS_OK;
   }
 
   BlocklistSaveInfo saveInfo;
   saveInfo.success = true;
-  nsresult rv;
   rv = NS_NewAtomicFileOutputStream(getter_AddRefs(saveInfo.outputStream),
                                     mBackingFile, -1, -1, 0);
   if (NS_FAILED(rv)) {
     return rv;
   }
   mBlocklist.EnumerateEntries(ProcessEntry, &saveInfo);
   if (!saveInfo.success) {
     PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
@@ -446,16 +476,22 @@ CertBlocklist::SaveEntries()
 //                       [const, array, size_is(serialLength)] in octet serial,
 //                       in unsigned long serialLength);
 NS_IMETHODIMP CertBlocklist::IsCertRevoked(const uint8_t* aIssuer, uint32_t aIssuerLength,
                                            const uint8_t* aSerial, uint32_t aSerialLength,
                                            bool* _retval)
 {
   mozilla::MutexAutoLock lock(mMutex);
 
+  nsresult rv = EnsureBackingFileInitialized(lock);
+
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   mozilla::pkix::Input issuer;
   mozilla::pkix::Input serial;
   if (issuer.Init(aIssuer, aIssuerLength) != mozilla::pkix::Success) {
     return NS_ERROR_FAILURE;
   }
   if (serial.Init(aSerial, aSerialLength) != mozilla::pkix::Success) {
     return NS_ERROR_FAILURE;
   }
--- a/security/manager/boot/src/CertBlocklist.h
+++ b/security/manager/boot/src/CertBlocklist.h
@@ -57,15 +57,18 @@ public:
 private:
   BlocklistTable mBlocklist;
   nsresult AddRevokedCertInternal(const char* aIssuer,
                                   const char* aSerial,
                                   CertBlocklistItemState aItemState,
                                   mozilla::MutexAutoLock& /*proofOfLock*/);
   mozilla::Mutex mMutex;
   bool mModified;
+  // Use Get
+  bool mBackingIsInitialized;
+  nsresult EnsureBackingFileInitialized(mozilla::MutexAutoLock& lock);
   nsCOMPtr<nsIFile> mBackingFile;
 
 protected:
   virtual ~CertBlocklist();
 };
 
 #endif // CertBlocklist_h
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -4,21 +4,24 @@
  * 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/. */
 
 #include "nsNSSComponent.h"
 
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "mozilla/Telemetry.h"
+#include "nsAppDirectoryServiceDefs.h"
 #include "nsCertVerificationThread.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsICertOverrideService.h"
+#include "NSSCertDBTrustDomain.h"
+#include "nsThreadUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsThreadUtils.h"
 #include "mozilla/PublicSSL.h"
 #include "mozilla/StaticPtr.h"
 
 #ifndef MOZ_NO_SMART_CARDS
 #include "nsSmartCardMonitor.h"
 #endif
@@ -1073,16 +1076,22 @@ nsNSSComponent::InitializeNSS()
                        Preferences::GetBool("security.ssl.enable_alpn",
                                             ALPN_ENABLED_DEFAULT));
 
   if (NS_FAILED(InitializeCipherSuite())) {
     PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("Unable to initialize cipher suite settings\n"));
     return NS_ERROR_FAILURE;
   }
 
+  // ensure the CertBlocklist is initialised
+  nsCOMPtr<nsICertBlocklist> certList = do_GetService(NS_CERTBLOCKLIST_CONTRACTID);
+  if (!certList) {
+    return NS_ERROR_FAILURE;
+  }
+
   // dynamic options from prefs
   setValidationOptions(true, lock);
 
   mHttpForNSS.initTable();
 
 #ifndef MOZ_NO_SMART_CARDS
   LaunchSmartCardThreads();
 #endif