Bug 995069 - Use getrandom system call on Linux if available instead of opening /dev/urandom. r=cpeterson, a=jcristau
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 07 Feb 2017 11:30:54 +0100
changeset 378341 7ea61ef9175dca2d2025942c85ecf66df53a2554
parent 378340 468bfd99fb224469babf17b156e53fbe4b23e854
child 378342 123ae818f185e1d132d9243a60713316a87bb364
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpeterson, jcristau
bugs995069
milestone53.0a2
Bug 995069 - Use getrandom system call on Linux if available instead of opening /dev/urandom. r=cpeterson, a=jcristau
js/src/jsmath.cpp
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -51,16 +51,50 @@
 #endif
 
 #if defined(ANDROID) || defined(XP_DARWIN) || defined(__DragonFly__) || \
     defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
 # include <stdlib.h>
 # define HAVE_ARC4RANDOM
 #endif
 
+#if defined(__linux__)
+# include <linux/random.h> // For GRND_NONBLOCK.
+# include <sys/syscall.h> // For SYS_getrandom.
+
+// Older glibc versions don't define SYS_getrandom, so we define it here if
+// it's not available. See bug 995069.
+# if defined(__x86_64__)
+#  define GETRANDOM_NR 318
+# elif defined(__i386__)
+#  define GETRANDOM_NR 355
+# elif defined(__arm__)
+#  define GETRANDOM_NR 384
+# endif
+
+# if defined(SYS_getrandom)
+// We have SYS_getrandom. Use it to check GETRANDOM_NR. Only do this if we set
+// GETRANDOM_NR so tier 3 platforms with recent glibc are not forced to define
+// it for no good reason.
+#  if defined(GETRANDOM_NR)
+static_assert(GETRANDOM_NR == SYS_getrandom,
+              "GETRANDOM_NR should match the actual SYS_getrandom value");
+#  endif
+# else
+#  define SYS_getrandom GETRANDOM_NR
+# endif
+
+# if defined(GRND_NONBLOCK)
+static_assert(GRND_NONBLOCK == 1, "If GRND_NONBLOCK is not 1 the #define below is wrong");
+# else
+#  define GRND_NONBLOCK 1
+# endif
+
+#endif // defined(__linux__)
+
 using namespace js;
 
 using mozilla::Abs;
 using mozilla::NumberEqualsInt32;
 using mozilla::NumberIsInt32;
 using mozilla::ExponentComponent;
 using mozilla::FloatingPoint;
 using mozilla::IsFinite;
@@ -681,27 +715,38 @@ js::GenerateRandomSeed()
 {
     uint64_t seed = 0;
 
 #if defined(XP_WIN)
     MOZ_ALWAYS_TRUE(RtlGenRandom(&seed, sizeof(seed)));
 #elif defined(HAVE_ARC4RANDOM)
     seed = (static_cast<uint64_t>(arc4random()) << 32) | arc4random();
 #elif defined(XP_UNIX)
-    int fd = open("/dev/urandom", O_RDONLY);
-    if (fd >= 0) {
-        mozilla::Unused << read(fd, static_cast<void*>(&seed), sizeof(seed));
-        close(fd);
+    bool done = false;
+# if defined(__linux__)
+    // Try the relatively new getrandom syscall first. It's the preferred way
+    // on Linux as /dev/urandom may not work inside chroots and is harder to
+    // sandbox (see bug 995069).
+    int ret = syscall(SYS_getrandom, &seed, sizeof(seed), GRND_NONBLOCK);
+    done = (ret == sizeof(seed));
+# endif
+    if (!done) {
+        int fd = open("/dev/urandom", O_RDONLY);
+        if (fd >= 0) {
+            mozilla::Unused << read(fd, static_cast<void*>(&seed), sizeof(seed));
+            close(fd);
+        }
     }
 #else
 # error "Platform needs to implement GenerateRandomSeed()"
 #endif
 
     // Also mix in PRMJ_Now() in case we couldn't read random bits from the OS.
-    return seed ^ PRMJ_Now();
+    uint64_t timestamp = PRMJ_Now();
+    return seed ^ timestamp ^ (timestamp << 32);
 }
 
 void
 js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed)
 {
     // XorShift128PlusRNG must be initialized with a non-zero seed.
     do {
         seed[0] = GenerateRandomSeed();