From 9aa0ad981f3cb0b718d91fbdb5d5bdd6164c60d6 Mon Sep 17 00:00:00 2001 From: Dana Keeler Date: Wed, 29 Sep 2021 21:23:59 +0000 Subject: [PATCH] Bug 1705360 - "hide" NSS DBs from meddling third party software r=jschanck,bbeurdouche Some crash reports appear to be indicating that initializing NSS' certificate and key databases is taking on the order of minutes in some cases, which is unexpected. One hypothesis is that third-party software is opening these DBs at the same time that NSS is operating on them, causing contention and thus slowness. This patch experimentally (in Nightly only) renames these DBs in the hopes that third-party software might not recognize them as the DBs it's looking for, and will thus leave them alone. Differential Revision: https://phabricator.services.mozilla.com/D126028 --- .../browser_startup_mainthreadio.js | 46 +++++++---- .../migration/FirefoxProfileMigrator.jsm | 1 + .../certverifier/NSSCertDBTrustDomain.cpp | 7 +- security/certverifier/NSSCertDBTrustDomain.h | 2 +- security/manager/ssl/nsNSSComponent.cpp | 79 +++++++++++++++++-- .../ssl/tests/unit/test_db_format_pref_new.js | 24 ++++-- 6 files changed, 126 insertions(+), 33 deletions(-) diff --git a/browser/base/content/test/performance/browser_startup_mainthreadio.js b/browser/base/content/test/performance/browser_startup_mainthreadio.js index 2ebac5264108..83ee9fce0ed6 100644 --- a/browser/base/content/test/performance/browser_startup_mainthreadio.js +++ b/browser/base/content/test/performance/browser_startup_mainthreadio.js @@ -33,6 +33,8 @@ const LINUX = AppConstants.platform == "linux"; const WIN = AppConstants.platform == "win"; const MAC = AppConstants.platform == "macosx"; +const kNSSDBPrefix = AppConstants.NIGHTLY_BUILD ? "gecko-no-share-" : ""; + const kSharedFontList = SpecialPowers.getBoolPref("gfx.e10s.font-list.shared"); /* This is an object mapping string phases of startup to lists of known cases @@ -357,14 +359,20 @@ const startupPhases = { }, { // bug 1370516 - NSS should be initialized off main thread. - path: "ProfD:cert9.db", - condition: WIN, - read: 5, - stat: 4, + path: `ProfD:cert9.db`, + condition: WIN && AppConstants.NIGHTLY_BUILD, + stat: 1, }, { // bug 1370516 - NSS should be initialized off main thread. - path: "ProfD:cert9.db", + path: `ProfD:${kNSSDBPrefix}cert9.db`, + condition: WIN, + read: 5, + stat: AppConstants.NIGHTLY_BUILD ? 5 : 4, + }, + { + // bug 1370516 - NSS should be initialized off main thread. + path: `ProfD:${kNSSDBPrefix}cert9.db`, condition: WIN, ignoreIfUnused: true, // if canonicalize(ProfD) == ProfD, we'll use the previous entry. canonicalize: true, @@ -372,14 +380,14 @@ const startupPhases = { }, { // bug 1370516 - NSS should be initialized off main thread. - path: "ProfD:cert9.db-journal", + path: `ProfD:${kNSSDBPrefix}cert9.db-journal`, condition: WIN, canonicalize: true, stat: 3, }, { // bug 1370516 - NSS should be initialized off main thread. - path: "ProfD:cert9.db-wal", + path: `ProfD:${kNSSDBPrefix}cert9.db-wal`, condition: WIN, canonicalize: true, stat: 3, @@ -392,14 +400,20 @@ const startupPhases = { }, { // bug 1370516 - NSS should be initialized off main thread. - path: "ProfD:key4.db", - condition: WIN, - read: 8, - stat: 4, + path: `ProfD:key4.db`, + condition: WIN && AppConstants.NIGHTLY_BUILD, + stat: 1, }, { // bug 1370516 - NSS should be initialized off main thread. - path: "ProfD:key4.db", + path: `ProfD:${kNSSDBPrefix}key4.db`, + condition: WIN, + read: 8, + stat: AppConstants.NIGHTLY_BUILD ? 5 : 4, + }, + { + // bug 1370516 - NSS should be initialized off main thread. + path: `ProfD:${kNSSDBPrefix}key4.db`, condition: WIN, ignoreIfUnused: true, // if canonicalize(ProfD) == ProfD, we'll use the previous entry. canonicalize: true, @@ -407,14 +421,14 @@ const startupPhases = { }, { // bug 1370516 - NSS should be initialized off main thread. - path: "ProfD:key4.db-journal", + path: `ProfD:${kNSSDBPrefix}key4.db-journal`, condition: WIN, canonicalize: true, stat: 5, }, { // bug 1370516 - NSS should be initialized off main thread. - path: "ProfD:key4.db-wal", + path: `ProfD:${kNSSDBPrefix}key4.db-wal`, condition: WIN, canonicalize: true, stat: 5, @@ -527,13 +541,13 @@ const startupPhases = { write: 1300, }, { - path: "ProfD:key4.db-journal", + path: `ProfD:${kNSSDBPrefix}key4.db-journal`, condition: WIN, canonicalize: true, stat: 2, }, { - path: "ProfD:key4.db-wal", + path: `ProfD:${kNSSDBPrefix}key4.db-wal`, condition: WIN, canonicalize: true, stat: 2, diff --git a/browser/components/migration/FirefoxProfileMigrator.jsm b/browser/components/migration/FirefoxProfileMigrator.jsm index bba617374f2f..7844b50ed81d 100644 --- a/browser/components/migration/FirefoxProfileMigrator.jsm +++ b/browser/components/migration/FirefoxProfileMigrator.jsm @@ -171,6 +171,7 @@ FirefoxProfileMigrator.prototype._getResourcesInternal = function( "logins.json", "key3.db", "key4.db", + "gecko-no-share-key4.db", ]); let formData = getFileResource(types.FORMDATA, [ "formhistory.sqlite", diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index a26121054c5a..19419467f4b1 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -1548,7 +1548,8 @@ void NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension, } SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig, - PKCS11DBConfig pkcs11DbConfig) { + PKCS11DBConfig pkcs11DbConfig, + const char* nssDBPrefix) { MOZ_ASSERT(NS_IsMainThread()); // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs @@ -1568,8 +1569,8 @@ SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig, MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(), (int)nssDbConfig, (int)pkcs11DbConfig)); - SECStatus srv = - NSS_Initialize(dbTypeAndDirectory.get(), "", "", SECMOD_DB, flags); + SECStatus srv = NSS_Initialize(dbTypeAndDirectory.get(), nssDBPrefix, + nssDBPrefix, SECMOD_DB, flags); if (srv != SECSuccess) { return srv; } diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h index fa24b85f2e24..17d6e9168d44 100644 --- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -49,7 +49,7 @@ enum class NetscapeStepUpPolicy : uint32_t { }; SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig, - PKCS11DBConfig pkcs11DbConfig); + PKCS11DBConfig pkcs11DbConfig, const char* nssDBPrefix); void DisableMD5(); diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index b62aa07dc965..ccbfb2ce2f39 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -1757,7 +1757,8 @@ static nsresult AttemptToRenamePKCS11ModuleDB(const nsACString& profilePath) { // there anyway. // |profilePath| is encoded in UTF-8. static nsresult InitializeNSSWithFallbacks(const nsACString& profilePath, - bool nocertdb, bool safeMode) { + bool nocertdb, bool safeMode, + const char* nssDBPrefix) { if (nocertdb || profilePath.IsEmpty()) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nocertdb mode or empty profile path -> NSS_NoDB_Init")); @@ -1778,7 +1779,7 @@ static nsresult InitializeNSSWithFallbacks(const nsACString& profilePath, PKCS11DBConfig safeModeDBConfig = safeMode ? PKCS11DBConfig::DoNotLoadModules : PKCS11DBConfig::LoadModules; SECStatus srv = ::mozilla::psm::InitializeNSS( - profilePath, NSSDBConfig::ReadWrite, safeModeDBConfig); + profilePath, NSSDBConfig::ReadWrite, safeModeDBConfig, nssDBPrefix); if (srv == SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized NSS in r/w mode")); return NS_OK; @@ -1789,7 +1790,7 @@ static nsresult InitializeNSSWithFallbacks(const nsACString& profilePath, #endif // ifndef ANDROID // That failed. Try read-only mode. srv = ::mozilla::psm::InitializeNSS(profilePath, NSSDBConfig::ReadOnly, - safeModeDBConfig); + safeModeDBConfig, nssDBPrefix); if (srv == SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized NSS in r-o mode")); return NS_OK; @@ -1817,7 +1818,8 @@ static nsresult InitializeNSSWithFallbacks(const nsACString& profilePath, // flags causes NSS initialization to fail, so unfortunately we have to use // read-write mode. srv = ::mozilla::psm::InitializeNSS(profilePath, NSSDBConfig::ReadWrite, - PKCS11DBConfig::DoNotLoadModules); + PKCS11DBConfig::DoNotLoadModules, + nssDBPrefix); if (srv == SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("FIPS may be the problem")); // Unload NSS so we can attempt to fix this situation for the user. @@ -1844,13 +1846,15 @@ static nsresult InitializeNSSWithFallbacks(const nsACString& profilePath, return rv; } srv = ::mozilla::psm::InitializeNSS(profilePath, NSSDBConfig::ReadWrite, - PKCS11DBConfig::LoadModules); + PKCS11DBConfig::LoadModules, + nssDBPrefix); if (srv == SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized in r/w mode")); return NS_OK; } srv = ::mozilla::psm::InitializeNSS(profilePath, NSSDBConfig::ReadOnly, - PKCS11DBConfig::LoadModules); + PKCS11DBConfig::LoadModules, + nssDBPrefix); if (srv == SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized in r-o mode")); return NS_OK; @@ -1870,6 +1874,60 @@ static nsresult InitializeNSSWithFallbacks(const nsACString& profilePath, return srv == SECSuccess ? NS_OK : NS_ERROR_FAILURE; } +#ifdef NIGHTLY_BUILD +const char* sGeckoNSSDBPrefix = "gecko-no-share-"; +#else +const char* sGeckoNSSDBPrefix = ""; +#endif + +#ifdef NIGHTLY_BUILD +// dbType is either "cert9.db" or "key4.db" +void MigrateOneCertDB(const nsCOMPtr& profileDirectory, + const nsACString& dbType) { + nsCOMPtr newDBFile; + nsresult rv = profileDirectory->Clone(getter_AddRefs(newDBFile)); + if (NS_FAILED(rv)) { + return; + } + nsAutoCString newDBFilename(sGeckoNSSDBPrefix); + newDBFilename.Append(dbType); + rv = newDBFile->AppendNative(newDBFilename); + if (NS_FAILED(rv)) { + return; + } + bool exists; + rv = newDBFile->Exists(&exists); + if (NS_FAILED(rv)) { + return; + } + // If the prefixed DB already exists, don't overwrite it. + if (exists) { + return; + } + nsCOMPtr oldDBFile; + rv = profileDirectory->Clone(getter_AddRefs(oldDBFile)); + if (NS_FAILED(rv)) { + return; + } + rv = oldDBFile->AppendNative(dbType); + if (NS_FAILED(rv)) { + return; + } + Unused << oldDBFile->MoveToNative(nullptr, newDBFilename); +} + +void MigrateToPrefixedCertDBs() { + nsCOMPtr profileDirectory; + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(profileDirectory)); + if (NS_FAILED(rv)) { + return; + } + MigrateOneCertDB(profileDirectory, "cert9.db"_ns); + MigrateOneCertDB(profileDirectory, "key4.db"_ns); +} +#endif // NIGHTLY_BUILD + nsresult nsNSSComponent::InitializeNSS() { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent::InitializeNSS\n")); AUTO_PROFILER_LABEL("nsNSSComponent::InitializeNSS", OTHER); @@ -1891,6 +1949,12 @@ nsresult nsNSSComponent::InitializeNSS() { return NS_ERROR_NOT_AVAILABLE; } +#ifdef NIGHTLY_BUILD + if (!profileStr.IsEmpty()) { + MigrateToPrefixedCertDBs(); + } +#endif + #if defined(XP_WIN) || (defined(XP_LINUX) && !defined(ANDROID)) SetNSSDatabaseCacheModeAsAppropriate(); #endif @@ -1910,7 +1974,8 @@ nsresult nsNSSComponent::InitializeNSS() { } MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("inSafeMode: %u\n", inSafeMode)); - rv = InitializeNSSWithFallbacks(profileStr, nocertdb, inSafeMode); + rv = InitializeNSSWithFallbacks(profileStr, nocertdb, inSafeMode, + sGeckoNSSDBPrefix); MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); if (NS_FAILED(rv)) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed to initialize NSS")); diff --git a/security/manager/ssl/tests/unit/test_db_format_pref_new.js b/security/manager/ssl/tests/unit/test_db_format_pref_new.js index b242ad08321e..7c0e1188b3d9 100644 --- a/security/manager/ssl/tests/unit/test_db_format_pref_new.js +++ b/security/manager/ssl/tests/unit/test_db_format_pref_new.js @@ -10,13 +10,25 @@ function run_test() { let profileDir = do_get_profile(); let certificateDBFile = profileDir.clone(); - certificateDBFile.append("cert9.db"); - ok(!certificateDBFile.exists(), "cert9.db should not exist beforehand"); + let certificateDBName = AppConstants.NIGHTLY_BUILD + ? "gecko-no-share-cert9.db" + : "cert9.db"; + certificateDBFile.append(certificateDBName); + ok( + !certificateDBFile.exists(), + `${certificateDBName} should not exist beforehand` + ); let keyDBFile = profileDir.clone(); - keyDBFile.append("key4.db"); - ok(!keyDBFile.exists(), "key4.db should not exist beforehand"); + let keyDBName = AppConstants.NIGHTLY_BUILD + ? "gecko-no-share-key4.db" + : "key4.db"; + keyDBFile.append(keyDBName); + ok(!keyDBFile.exists(), `${keyDBName} should not exist beforehand`); // This should start PSM. Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports); - ok(certificateDBFile.exists(), "cert9.db should exist in the profile"); - ok(keyDBFile.exists(), "key4.db should exist in the profile"); + ok( + certificateDBFile.exists(), + `${certificateDBName} should exist in the profile` + ); + ok(keyDBFile.exists(), `${keyDBName} should exist in the profile`); }