on Linux dynamically detect if getrandom is supported and fall back to /dev/urandom if needed

This commit is contained in:
daan 2020-01-06 22:08:21 -08:00
parent d8d69c2c94
commit 4223caac0f

View file

@ -155,9 +155,9 @@ uintptr_t _mi_random_next(mi_random_ctx_t* ctx) {
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
To initialize a fresh random context we rely on the OS: To initialize a fresh random context we rely on the OS:
- windows: BCryptGenRandom - Windows : BCryptGenRandom
- bsd,wasi: arc4random_buf - osX,bsd,wasi: arc4random_buf
- linux: getrandom - Linux : getrandom,/dev/urandom
If we cannot get good randomness, we fall back to weak randomness based on a timer and ASLR. If we cannot get good randomness, we fall back to weak randomness based on a timer and ASLR.
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
@ -185,9 +185,47 @@ static bool os_random_buf(void* buf, size_t buf_len) {
return true; return true;
} }
#elif defined(__linux__) #elif defined(__linux__)
#include <sys/random.h> #include <sys/syscall.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
static bool os_random_buf(void* buf, size_t buf_len) { static bool os_random_buf(void* buf, size_t buf_len) {
return (getrandom(buf, buf_len, GRND_NONBLOCK) == (ssize_t)buf_len); // Modern Linux provides `getrandom` but different distributions either use `sys/random.h` or `linux/random.h`
// and for the latter the actual `getrandom` call is not always defined.
// (see <https://stackoverflow.com/questions/45237324/why-doesnt-getrandom-compile>)
// We therefore use a syscall directly and fall back dynamically to /dev/urandom when needed.
#ifdef SYS_getrandom
#ifndef GRND_NONBLOCK
#define GRND_NONBLOCK (1)
#endif
static volatile _Atomic(uintptr_t) no_getrandom; // = 0
if (mi_atomic_read(&no_getrandom)==0) {
ssize_t ret = syscall(SYS_getrandom, buf, buf_len, GRND_NONBLOCK);
if (ret >= 0) return (buf_len == (size_t)ret);
if (ret != ENOSYS) return false;
mi_atomic_write(&no_getrandom,1); // don't call again, and fall back to /dev/urandom
}
#endif
int flags = O_RDONLY;
#if defined(O_CLOEXEC)
flags |= O_CLOEXEC;
#endif
int fd = open("/dev/urandom", flags, 0);
if (fd < 0) return false;
size_t count = 0;
while(count < buf_len) {
ssize_t ret = read(fd, (char*)buf + count, buf_len - count);
if (ret<=0) {
if (errno!=EAGAIN && errno!=EINTR) break;
}
else {
count += ret;
}
}
close(fd);
return (count==buf_len);
} }
#else #else
static bool os_random_buf(void* buf, size_t buf_len) { static bool os_random_buf(void* buf, size_t buf_len) {