Handle linux getrandom returning EINTR on interrupts/signals

Also handle ENOTSUP failures where aria2 was build with linux headers
newer than the actual running kernel.

Fixes GH-336
This commit is contained in:
Nils Maier 2015-02-06 17:33:22 +01:00
parent 9cbbe9f1bb
commit ba0e32abae
3 changed files with 49 additions and 7 deletions

View File

@ -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<BYTE*>(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<result_type*>(buf);
size_t q = len / sizeof(result_type);
auto gen = std::uniform_int_distribution<result_type>();

View File

@ -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__

View File

@ -37,14 +37,42 @@
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/random.h>
#include <errno.h>
#include <linux/errno.h>
#include <stdint.h>
#include <stdio.h>
#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;
}