diff --git a/src/SimpleRandomizer.cc b/src/SimpleRandomizer.cc index 746491f1..31784c3e 100644 --- a/src/SimpleRandomizer.cc +++ b/src/SimpleRandomizer.cc @@ -42,6 +42,8 @@ #include "a2time.h" #include "a2functional.h" +#include "LogFactory.h" +#include "fmt.h" #ifdef HAVE_GETRANDOM_INTERFACE # include "getrandom_linux.h" @@ -88,10 +90,24 @@ void SimpleRandomizer::getRandomBytes(unsigned char* buf, size_t len) #ifdef __MINGW32__ BOOL r = CryptGenRandom(provider_, len, reinterpret_cast(buf)); assert(r); -#elif defined(HAVE_GETRANDOM_INTERFACE) - auto rv = getrandom_linux(buf, len); - assert(rv >= 0 && (size_t)rv == len); #else // ! __MINGW32__ +#if defined(HAVE_GETRANDOM_INTERFACE) + static bool have_random_support = true; + if (have_random_support) { + auto rv = getrandom_linux(buf, len); + if (rv != -1 || errno != ENOSYS) { + if (rv < -1) { + A2_LOG_ERROR(fmt("Failed to produce randomness: %d", errno)); + } + assert(rv >= 0 && (size_t)rv == len); + return; + } + have_random_support = false; + A2_LOG_INFO("Disabled getrandom support, because kernel does not "\ + "implement this feature (ENOSYS)"); + } + // Fall through to generic implementation +#endif // defined(HAVE_GETRANDOM_INTERFACE) auto ubuf = reinterpret_cast(buf); size_t q = len / sizeof(result_type); auto gen = std::uniform_int_distribution(); diff --git a/src/SimpleRandomizer.h b/src/SimpleRandomizer.h index 65ac2094..b3896ebd 100644 --- a/src/SimpleRandomizer.h +++ b/src/SimpleRandomizer.h @@ -54,8 +54,6 @@ private: private: #ifdef __MINGW32__ HCRYPTPROV provider_; -#elif defined(HAVE_GETRANDOM_INTERFACE) - // Nothing special needed #else std::random_device dev_; #endif // ! __MINGW32__ diff --git a/src/getrandom_linux.c b/src/getrandom_linux.c index 85314fd6..cbf4c32a 100644 --- a/src/getrandom_linux.c +++ b/src/getrandom_linux.c @@ -37,14 +37,42 @@ #include #include #include +#include +#include +#include +#include #include "config.h" #include "getrandom_linux.h" int getrandom_linux(void *buf, size_t buflen) { + int rv = 0; + uint8_t* p = buf; + while (buflen) { + int read; #ifdef HAVE_GETRANDOM - return getrandom(buf, buflen, 0); + read = getrandom(p, buflen, 0); #else // HAVE_GETRANDOM - return syscall(SYS_getrandom, buf, buflen, 0); + read = syscall(SYS_getrandom, p, buflen, 0); + /* Some libc impl. might mess this up */ + if (read == -EINTR || read == -ERESTART) { + errno = EINTR; + read = -1; + } + if (read < -1) { + errno = -read; + read = -1; + } #endif // HAVE_GETRANDOM + if (read < 0) { + if (errno == EINTR) { + continue; + } + return -1; + } + p += read; + rv += read; + buflen -= read; + } + return rv; }