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>
This commit is contained in:
Alessandro Ghedini 2016-07-09 13:02:18 +01:00 committed by CQ bot account: commit-bot@chromium.org
parent 9ef99d5656
commit 5e393fedef
2 changed files with 60 additions and 7 deletions

View File

@ -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 @@ static void init_once(void) {
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 @@ static struct rand_buffer *get_thread_local_buffer(void) {
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 @@ static struct rand_buffer *get_thread_local_buffer(void) {
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 @@ static void read_from_buffer(struct rand_buffer *buf,
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 @@ void CRYPTO_sysrand(uint8_t *out, size_t requested) {
}
}
if (!read_full(urandom_fd, out, requested)) {
if (!fill_with_entropy(out, requested)) {
abort();
}
}

View File

@ -114,6 +114,10 @@ extern "C" {
#define OPENSSL_WINDOWS
#endif
#if defined(__linux__)
#define OPENSSL_LINUX
#endif
#if defined(TRUSTY)
#define OPENSSL_TRUSTY
#define OPENSSL_NO_THREADS