Test getrandom(2) on Linux if available

This patch changes the urandom PRNG to read one byte from the
getrandom(2) Linux syscall on initialization in order to find any
unexpected behavior.

Change-Id: I8ef676854dc361e4f77527b53d1a14fd14d449a8
Reviewed-on: https://boringssl-review.googlesource.com/8681
Reviewed-by: Adam Langley <alangley@gmail.com>
Commit-Queue: Adam Langley <alangley@gmail.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/rand/urandom.c b/crypto/rand/urandom.c
index 4c8a5f7..b1ab13e 100644
--- a/crypto/rand/urandom.c
+++ b/crypto/rand/urandom.c
@@ -12,6 +12,8 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#define _GNU_SOURCE  /* needed for syscall() on Linux. */
+
 #include <openssl/rand.h>
 
 #if !defined(OPENSSL_WINDOWS) && !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
@@ -22,6 +24,10 @@
 #include <string.h>
 #include <unistd.h>
 
+#if defined(OPENSSL_LINUX)
+#include <sys/syscall.h>
+#endif
+
 #include <openssl/thread.h>
 #include <openssl/mem.h>
 
@@ -29,6 +35,43 @@
 #include "../internal.h"
 
 
+#if defined(OPENSSL_LINUX)
+
+#if defined(OPENSSL_X86_64)
+#define EXPECTED_SYS_getrandom 318
+#elif defined(OPENSSL_X86)
+#define EXPECTED_SYS_getrandom 355
+#elif defined(OPENSSL_AARCH64)
+#define EXPECTED_SYS_getrandom 278
+#elif defined(OPENSSL_ARM)
+#define EXPECTED_SYS_getrandom 384
+#elif defined(OPENSSL_PPC64LE)
+#define EXPECTED_SYS_getrandom 359
+#endif
+
+#if defined(EXPECTED_SYS_getrandom)
+#define USE_SYS_getrandom
+
+#if defined(SYS_getrandom)
+
+#if SYS_getrandom != EXPECTED_SYS_getrandom
+#error "system call number for getrandom is not the expected value"
+#endif
+
+#else  /* SYS_getrandom */
+
+#define SYS_getrandom EXPECTED_SYS_getrandom
+
+#endif  /* SYS_getrandom */
+
+#endif /* EXPECTED_SYS_getrandom */
+
+#if !defined(GRND_NONBLOCK)
+#define GRND_NONBLOCK 1
+#endif
+
+#endif  /* OPENSSL_LINUX */
+
 /* This file implements a PRNG by reading from /dev/urandom, optionally with a
  * buffer, which is unsafe across |fork|. */
 
@@ -71,6 +114,12 @@
   int fd = urandom_fd_requested;
   CRYPTO_STATIC_MUTEX_unlock_read(&requested_lock);
 
+#if defined(USE_SYS_getrandom)
+  /* Initial test of getrandom to find any unexpected behavior. */
+  uint8_t dummy;
+  syscall(SYS_getrandom, &dummy, sizeof(dummy), GRND_NONBLOCK);
+#endif
+
   if (fd == -2) {
     do {
       fd = open("/dev/urandom", O_RDONLY);
@@ -144,7 +193,7 @@
   if (buf == NULL) {
     return NULL;
   }
-  buf->used = BUF_SIZE;  /* To trigger a |read_full| on first use. */
+  buf->used = BUF_SIZE;  /* To trigger a |fill_with_entropy| on first use. */
   if (!CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_URANDOM_BUF, buf,
                                OPENSSL_free)) {
     OPENSSL_free(buf);
@@ -154,14 +203,14 @@
   return buf;
 }
 
-/* read_full reads exactly |len| bytes from |fd| into |out| and returns 1. In
- * the case of an error it returns 0. */
-static char read_full(int fd, uint8_t *out, size_t len) {
+/* fill_with_entropy writes |len| bytes of entropy into |out|. It returns one
+ * on success and zero on error. */
+static char fill_with_entropy(uint8_t *out, size_t len) {
   ssize_t r;
 
   while (len > 0) {
     do {
-      r = read(fd, out, len);
+      r = read(urandom_fd, out, len);
     } while (r == -1 && errno == EINTR);
 
     if (r <= 0) {
@@ -186,7 +235,7 @@
     out += remaining;
     requested -= remaining;
 
-    if (!read_full(urandom_fd, buf->rand, BUF_SIZE)) {
+    if (!fill_with_entropy(buf->rand, BUF_SIZE)) {
       abort();
       return;
     }
@@ -213,7 +262,7 @@
     }
   }
 
-  if (!read_full(urandom_fd, out, requested)) {
+  if (!fill_with_entropy(out, requested)) {
     abort();
   }
 }
diff --git a/include/openssl/base.h b/include/openssl/base.h
index cb847d5..71f283d 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -114,6 +114,10 @@
 #define OPENSSL_WINDOWS
 #endif
 
+#if defined(__linux__)
+#define OPENSSL_LINUX
+#endif
+
 #if defined(TRUSTY)
 #define OPENSSL_TRUSTY
 #define OPENSSL_NO_THREADS