Bug 935831: Read from the Unix fd directly to bypass stdio buffering NSS_3_15_4_BETA5
authorWan-Teh Chang <wtc@google.com>
Tue, 26 Nov 2013 17:50:50 -0800
changeset 10949 44ba62b87bd6a3aedfe6d1d55a510a65e59b0fcf
parent 10948 661e0159d3f9f23d770cd0dc8b9c47141b83a216
child 10950 4baab74227d7e6ae178cef2adc6068b0bd3c07b7
push id229
push userwtc@google.com
push dateWed, 27 Nov 2013 01:50:58 +0000
bugs935831
Bug 935831: Read from the Unix fd directly to bypass stdio buffering and work around the fread bug in unbuffered I/O mode on Android. r=brian.
lib/freebl/unix_rand.c
--- a/lib/freebl/unix_rand.c
+++ b/lib/freebl/unix_rand.c
@@ -954,39 +954,44 @@ void RNG_SystemInfoForRNG(void)
 
 }
 
 #define TOTAL_FILE_LIMIT 1000000	/* one million */
 
 size_t RNG_FileUpdate(const char *fileName, size_t limit)
 {
     FILE *        file;
-    size_t        bytes;
+    int           fd;
+    int           bytes;
     size_t        fileBytes = 0;
     struct stat   stat_buf;
     unsigned char buffer[BUFSIZ];
     static size_t totalFileBytes = 0;
     
     /* suppress valgrind warnings due to holes in struct stat */
     memset(&stat_buf, 0, sizeof(stat_buf));
 
     if (stat((char *)fileName, &stat_buf) < 0)
 	return fileBytes;
     RNG_RandomUpdate(&stat_buf, sizeof(stat_buf));
     
     file = fopen(fileName, "r");
     if (file != NULL) {
-	/* Set buffering mode to unbuffered I/O to avoid reading more bytes
-	 * than we need from /dev/urandom. Moreover, we read into a buffer
-	 * of size BUFSIZ, so buffered I/O has no performance advantage. */
-	setvbuf(file, NULL, _IONBF, 0);
+	/* Read from the underlying file descriptor directly to bypass stdio
+	 * buffering and avoid reading more bytes than we need from
+	 * /dev/urandom. NOTE: we can't use fread with unbuffered I/O because
+	 * fread may return EOF in unbuffered I/O mode on Android.
+	 *
+	 * Moreover, we read into a buffer of size BUFSIZ, so buffered I/O
+	 * has no performance advantage. */
+	fd = fileno(file);
 	while (limit > fileBytes) {
 	    bytes = PR_MIN(sizeof buffer, limit - fileBytes);
-	    bytes = fread(buffer, 1, bytes, file);
-	    if (bytes == 0) 
+	    bytes = read(fd, buffer, bytes);
+	    if (bytes <= 0)
 		break;
 	    RNG_RandomUpdate(buffer, bytes);
 	    fileBytes      += bytes;
 	    totalFileBytes += bytes;
 	    /* after TOTAL_FILE_LIMIT has been reached, only read in first
 	    ** buffer of data from each subsequent file.
 	    */
 	    if (totalFileBytes > TOTAL_FILE_LIMIT) 
@@ -1125,31 +1130,35 @@ static void rng_systemJitter(void)
    } else {
 	fileToRead++;
    }
 }
 
 size_t RNG_SystemRNG(void *dest, size_t maxLen)
 {
     FILE *file;
-    size_t bytes;
+    int fd;
+    int bytes;
     size_t fileBytes = 0;
     unsigned char *buffer = dest;
 
     file = fopen("/dev/urandom", "r");
     if (file == NULL) {
 	return rng_systemFromNoise(dest, maxLen);
     }
-    /* Set buffering mode to unbuffered I/O to avoid reading more bytes
-     * than we need from /dev/urandom. */
-    setvbuf(file, NULL, _IONBF, 0);
+    /* Read from the underlying file descriptor directly to bypass stdio
+     * buffering and avoid reading more bytes than we need from /dev/urandom.
+     * NOTE: we can't use fread with unbuffered I/O because fread may return
+     * EOF in unbuffered I/O mode on Android.
+     */
+    fd = fileno(file);
     while (maxLen > fileBytes) {
 	bytes = maxLen - fileBytes;
-	bytes = fread(buffer, 1, bytes, file);
-	if (bytes == 0) 
+	bytes = read(fd, buffer, bytes);
+	if (bytes <= 0)
 	    break;
 	fileBytes += bytes;
 	buffer += bytes;
     }
     fclose(file);
     if (fileBytes != maxLen) {
 	PORT_SetError(SEC_ERROR_NEED_RANDOM);  /* system RNG failed */
 	fileBytes = 0;