Reduce NSS impact on startup time on windows by a) not using file i/o to seed on platforms with a working cryptgenrandom, and b) not double-initializing the RNG.
authorJohnathan Nightingale <johnath@mozilla.com>
Tue, 14 Jul 2009 20:51:40 -0400
changeset 26065 d40be78ef5152a59ef6c31a8ee5dce3c44fd9e98
parent 26064 9d3a32d9ef1ea4a434aafca90e809130489515ff
child 26066 b91c9ee69e6ef73c54ada2aa2e97a7055c24d416
push id1772
push userjnightingale@mozilla.com
push dateWed, 15 Jul 2009 00:59:38 +0000
bugs489811, 501605
milestone1.9.1.1pre
Reduce NSS impact on startup time on windows by a) not using file i/o to seed on platforms with a working cryptgenrandom, and b) not double-initializing the RNG. Bug 489811 - RNG_SystemInfoForRNG called twice by nsc_CommonInitialize p=Nelson, r=rrelyea Bug 501605 - Very slow startup for Firefox 3.5 due to accessing IE Temporary Internet Files p=Nelson, r=julien, sr=wtc
security/nss/lib/freebl/win_rand.c
security/nss/lib/softoken/pkcs11.c
--- a/security/nss/lib/freebl/win_rand.c
+++ b/security/nss/lib/freebl/win_rand.c
@@ -53,16 +53,17 @@
 #include <stdio.h>
 #include "prio.h"
 #include "prerror.h"
 
 static PRInt32  filesToRead;
 static DWORD    totalFileBytes;
 static DWORD    maxFileBytes	= 250000;	/* 250 thousand */
 static DWORD    dwNumFiles, dwReadEvery, dwFileToRead;
+static PRBool   usedWindowsPRNG;
 
 static BOOL
 CurrentClockTickTime(LPDWORD lpdwHigh, LPDWORD lpdwLow)
 {
     LARGE_INTEGER   liCount;
 
     if (!QueryPerformanceCounter(&liCount))
         return FALSE;
@@ -126,55 +127,53 @@ size_t RNG_GetNoise(void *buf, size_t ma
     nBytes = sizeof(sTime) > maxbuf ? maxbuf : sizeof(sTime);
     memcpy(((char *)buf) + n, &sTime, nBytes);
     n += nBytes;
     }
 
     return n;
 }
 
-typedef PRInt32 (* Handler)(const char *);
+typedef PRInt32 (* Handler)(const PRUnichar *);
 #define MAX_DEPTH 2
+#define MAX_FOLDERS 4
+#define MAX_FILES 1024
 
 static void
 EnumSystemFilesInFolder(Handler func, PRUnichar* szSysDir, int maxDepth) 
 {
     int                 iContinue;
+    unsigned int        iFolders  = 0;
+    unsigned int        iFiles    = 0;
     HANDLE              lFindHandle;
     WIN32_FIND_DATAW    fdData;
     PRUnichar           szFileName[_MAX_PATH];
-    char                narrowFileName[_MAX_PATH];
 
     if (maxDepth < 0)
     	return;
-    // tack *.* on the end so we actually look for files. this will
-    // not overflow
-    wcscpy(szFileName, szSysDir);
-    wcscat(szFileName, L"\\*.*");
+    // append *.* so we actually look for files. this will not overflow
+    _snwprintf(szFileName, _MAX_PATH, L"%s\\*.*", szSysDir);
 
     lFindHandle = FindFirstFileW(szFileName, &fdData);
     if (lFindHandle == INVALID_HANDLE_VALUE)
         return;
     do {
 	iContinue = 1;
 	if (wcscmp(fdData.cFileName, L".") == 0 ||
             wcscmp(fdData.cFileName, L"..") == 0) {
 	    // skip "." and ".."
 	} else {
 	    // pass the full pathname to the callback
 	    _snwprintf(szFileName, _MAX_PATH, L"%s\\%s", szSysDir, 
 		       fdData.cFileName);
 	    if (fdData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
-		EnumSystemFilesInFolder(func, szFileName, maxDepth - 1);
+		if (++iFolders <= MAX_FOLDERS)
+		    EnumSystemFilesInFolder(func, szFileName, maxDepth - 1);
 	    } else {
-		iContinue = WideCharToMultiByte(CP_ACP, 0, szFileName, -1, 
-						narrowFileName, _MAX_PATH, 
-						NULL, NULL);
-		if (iContinue)
-		    iContinue = !(*func)(narrowFileName);
+		iContinue = (++iFiles <= MAX_FILES) && !(*func)(szFileName);
 	    }
 	}
 	if (iContinue)
 	    iContinue = FindNextFileW(lFindHandle, &fdData);
     } while (iContinue);
     FindClose(lFindHandle);
 }
 
@@ -182,17 +181,16 @@ static BOOL
 EnumSystemFiles(Handler func)
 {
     PRUnichar szSysDir[_MAX_PATH];
     static const int folders[] = {
     	CSIDL_BITBUCKET,  
 	CSIDL_RECENT,
 #ifndef WINCE		     
 	CSIDL_INTERNET_CACHE, 
-	CSIDL_COMPUTERSNEARME, 
 	CSIDL_HISTORY,
 #endif
 	0
     };
     int i = 0;
     if (_MAX_PATH > (i = GetTempPathW(_MAX_PATH, szSysDir))) {
         if (i > 0 && szSysDir[i-1] == L'\\')
 	    szSysDir[i-1] = L'\0'; // we need to lop off the trailing slash
@@ -203,66 +201,82 @@ EnumSystemFiles(Handler func)
         if (szSysDir[0])
             EnumSystemFilesInFolder(func, szSysDir, MAX_DEPTH);
         szSysDir[0] =  L'\0';
     }
     return PR_TRUE;
 }
 
 static PRInt32
-CountFiles(const char *file)
+CountFiles(const PRUnichar *file)
 {
     dwNumFiles++;
     return 0;
 }
 
-static void 
+static int
 ReadSingleFile(const char *filename)
 {
     PRFileDesc *    file;
     int             nBytes;
     unsigned char   buffer[1024];
 
     file = PR_Open(filename, PR_RDONLY, 0);
     if (file != NULL) {
 	while (PR_Read(file, buffer, sizeof buffer) > 0)
 	    ;
         PR_Close(file);
     }
+    return (file != NULL);
 }
 
 static PRInt32
-ReadOneFile(const char *file)
+ReadOneFile(const PRUnichar *szFileName)
 {
+    char narrowFileName[_MAX_PATH];
+
     if (dwNumFiles == dwFileToRead) {
-	ReadSingleFile(file);
+	int success = WideCharToMultiByte(CP_ACP, 0, szFileName, -1, 
+					  narrowFileName, _MAX_PATH, 
+					  NULL, NULL);
+	if (success)
+	    success = ReadSingleFile(narrowFileName);
+    	if (!success)
+	    dwFileToRead++; /* couldn't read this one, read the next one. */
     }
     dwNumFiles++;
     return dwNumFiles > dwFileToRead;
 }
 
 static PRInt32
-ReadFiles(const char *file)
+ReadFiles(const PRUnichar *szFileName)
 {
+    char narrowFileName[_MAX_PATH];
+
     if ((dwNumFiles % dwReadEvery) == 0) {
 	++filesToRead;
     }
     if (filesToRead) {
-	DWORD    prevFileBytes = totalFileBytes;
-        RNG_FileForRNG(file);
+	DWORD prevFileBytes = totalFileBytes;
+	int   iContinue     = WideCharToMultiByte(CP_ACP, 0, szFileName, -1, 
+						  narrowFileName, _MAX_PATH, 
+						  NULL, NULL);
+	if (iContinue) {
+	    RNG_FileForRNG(narrowFileName);
+	}
 	if (prevFileBytes < totalFileBytes) {
 	    --filesToRead;
 	}
     }
     dwNumFiles++;
     return (totalFileBytes >= maxFileBytes);
 }
 
 static void
-ReadSystemFiles()
+ReadSystemFiles(void)
 {
     // first count the number of files
     dwNumFiles = 0;
     if (!EnumSystemFiles(CountFiles))
         return;
 
     RNG_RandomUpdate(&dwNumFiles, sizeof(dwNumFiles));
 
@@ -272,16 +286,17 @@ ReadSystemFiles()
     if (dwNumFiles == 0)
         return;
 
     dwReadEvery = dwNumFiles / 10;
     if (dwReadEvery == 0)
         dwReadEvery = 1;  // less than 10 files
 
     dwNumFiles = 0;
+    totalFileBytes = 0;
     EnumSystemFiles(ReadFiles);
 }
 
 void RNG_SystemInfoForRNG(void)
 {
     DWORD           dwVal;
     char            buffer[256];
     int             nBytes;
@@ -344,18 +359,19 @@ void RNG_SystemInfoForRNG(void)
                          &dwNumClusters)) {
         RNG_RandomUpdate(&dwSectors,      sizeof(dwSectors));
         RNG_RandomUpdate(&dwBytes,        sizeof(dwBytes));
         RNG_RandomUpdate(&dwFreeClusters, sizeof(dwFreeClusters));
         RNG_RandomUpdate(&dwNumClusters,  sizeof(dwNumClusters));
     }
 #endif
 
-    // now let's do some files
-    ReadSystemFiles();
+    // Skip the potentially slow file scanning if the OS's PRNG worked.
+    if (!usedWindowsPRNG)
+	ReadSystemFiles();
 
     nBytes = RNG_GetNoise(buffer, 20);  // get up to 20 bytes
     RNG_RandomUpdate(buffer, nBytes);
 }
 
 static void rng_systemJitter(void)
 {   
     dwNumFiles = 0;
@@ -407,16 +423,17 @@ void RNG_FileForRNG(const char *filename
  * says CeGenRandom is the right function to call for creating a seed
  * for a random number generator.
  */
 size_t RNG_SystemRNG(void *dest, size_t maxLen)
 {
     size_t bytes = 0;
     if (CeGenRandom(maxLen, dest)) {
 	    bytes = maxLen;
+	    usedWindowsPRNG = PR_TRUE;
     }
     if (bytes == 0) {
 	bytes = rng_systemFromNoise(dest,maxLen);
     }
     return bytes;
 }
 
 
@@ -424,18 +441,16 @@ size_t RNG_SystemRNG(void *dest, size_t 
 
 void RNG_FileForRNG(const char *filename)
 {
     FILE*           file;
     int             nBytes;
     struct stat     stat_buf;
     unsigned char   buffer[1024];
 
-   /* static DWORD    totalFileBytes = 0; */
-
     /* windows doesn't initialize all the bytes in the stat buf,
      * so initialize them all here to avoid UMRs.
      */
     memset(&stat_buf, 0, sizeof stat_buf);
 
     if (stat((char *)filename, &stat_buf) < 0)
         return;
 
@@ -511,25 +526,27 @@ size_t RNG_SystemRNG(void *dest, size_t 
     HMODULE hModule;
     RtlGenRandomFn pRtlGenRandom;
     CryptAcquireContextAFn pCryptAcquireContextA;
     CryptReleaseContextFn pCryptReleaseContext;
     CryptGenRandomFn pCryptGenRandom;
     HCRYPTPROV hCryptProv;
     size_t bytes = 0;
 
+    usedWindowsPRNG = PR_FALSE;
     hModule = LoadLibrary("advapi32.dll");
     if (hModule == NULL) {
 	return rng_systemFromNoise(dest,maxLen);
     }
     pRtlGenRandom = (RtlGenRandomFn)
 	GetProcAddress(hModule, "SystemFunction036");
     if (pRtlGenRandom) {
 	if (pRtlGenRandom(dest, maxLen)) {
 	    bytes = maxLen;
+	    usedWindowsPRNG = PR_TRUE;
 	} else {
 	    bytes = rng_systemFromNoise(dest,maxLen);
 	}
 	goto done;
     }
     pCryptAcquireContextA = (CryptAcquireContextAFn)
 	GetProcAddress(hModule, "CryptAcquireContextA");
     pCryptReleaseContext = (CryptReleaseContextFn)
@@ -539,16 +556,17 @@ size_t RNG_SystemRNG(void *dest, size_t 
     if (!pCryptAcquireContextA || !pCryptReleaseContext || !pCryptGenRandom) {
 	bytes = rng_systemFromNoise(dest,maxLen);
 	goto done;
     }
     if (pCryptAcquireContextA(&hCryptProv, NULL, NULL,
 	PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
 	if (pCryptGenRandom(hCryptProv, maxLen, dest)) {
 	    bytes = maxLen;
+	    usedWindowsPRNG = PR_TRUE;
 	}
 	pCryptReleaseContext(hCryptProv, 0);
     }
     if (bytes == 0) {
 	bytes = rng_systemFromNoise(dest,maxLen);
     }
 done:
     FreeLibrary(hModule);
--- a/security/nss/lib/softoken/pkcs11.c
+++ b/security/nss/lib/softoken/pkcs11.c
@@ -2582,18 +2582,16 @@ CK_RV nsc_CommonInitialize(CK_VOID_PTR p
 	crv = CKR_DEVICE_ERROR;
 	return crv;
     }
     rv = BL_Init();             /* initialize freebl engine */
     if (rv != SECSuccess) {
 	crv = CKR_DEVICE_ERROR;
 	return crv;
     }
-    RNG_SystemInfoForRNG();
-
 
     /* NOTE:
      * we should be getting out mutexes from this list, not statically binding
      * them from NSPR. This should happen before we allow the internal to split
      * off from the rest on NSS.
      */
 
    /* initialize the key and cert db's */