From 5264aebb4f822fa9147ee576562a4961ca97261d Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 14 Aug 2017 10:33:06 +0200 Subject: [PATCH] lib/randutils: improve getrandom() usage The getrandom() does not have to return all requested bytes (missing entropy or when interrupted by signal). The current implementation in util-linux stupidly asks for all random data again, rather than only for missing bytes. The current code also does not care if we repeat our requests for ever; that's bad. This patch uses the same way as we already use for reading from /dev/urandom. It means: * repeat getrandom() for only missing bytes * limit number of unsuccessful request (16 times) * fallback to /dev/urandom on ENOSYS (old kernel or so...) Addresses: https://github.com/karelzak/util-linux/issues/496 Signed-off-by: Karel Zak --- lib/randutils.c | 66 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/lib/randutils.c b/lib/randutils.c index 7d85dc841..5cd1c9976 100644 --- a/lib/randutils.c +++ b/lib/randutils.c @@ -95,27 +95,39 @@ int random_get_fd(void) */ void random_get_bytes(void *buf, size_t nbytes) { - size_t i; unsigned char *cp = (unsigned char *)buf; + size_t i, n = nbytes; + int lose_counter = 0; #ifdef HAVE_GETRANDOM - errno = 0; - while (getrandom(buf, nbytes, 0) != (ssize_t)nbytes) { - if (errno == EINTR) + while (n > 0) { + int x; + + errno = 0; + x = getrandom(cp, n, 0); + if (x > 0) { /* success */ + n -= x; + cp += x; + lose_counter = 0; + } else if (errno == ENOSYS) { /* kernel without getrandom() */ + break; + } else { + if (lose_counter++ > 16) /* entropy problem? */ + break; continue; - break; + } } + if (errno == ENOSYS) - /* - * We've been built against headers that support getrandom, - * but the running kernel does not. - * Fallback to reading from /dev/{u,}random as before - */ #endif + + /* + * We've been built against headers that support getrandom, but the + * running kernel does not. Fallback to reading from /dev/{u,}random + * as before + */ { - size_t n = nbytes; int fd = random_get_fd(); - int lose_counter = 0; if (fd >= 0) { while (n > 0) { @@ -153,7 +165,6 @@ void random_get_bytes(void *buf, size_t nbytes) sizeof(ul_jrand_seed)-sizeof(unsigned short)); } #endif - return; } @@ -181,15 +192,32 @@ const char *random_tell_source(void) } #ifdef TEST_PROGRAM_RANDUTILS -int main(int argc __attribute__ ((__unused__)), - char *argv[] __attribute__ ((__unused__))) +int main(int argc, char *argv[]) { - unsigned int v, i; + size_t i, n; + int64_t *vp, v; + char *buf; + size_t bufsz; - /* generate and print 10 random numbers */ - for (i = 0; i < 10; i++) { + n = argc == 1 ? 16 : atoi(argv[1]); + + printf("Multiple random calls:\n"); + for (i = 0; i < n; i++) { random_get_bytes(&v, sizeof(v)); - printf("%d\n", v); + printf("#%02zu: %25ju\n", i, v); + } + + + printf("One random call:\n"); + bufsz = n * sizeof(*vp); + buf = malloc(bufsz); + if (!buf) + err(EXIT_FAILURE, "failed to allocate buffer"); + + random_get_bytes(buf, bufsz); + for (i = 0; i < n; i++) { + vp = (int64_t *) (buf + (i * sizeof(*vp))); + printf("#%02zu: %25ju\n", i, *vp); } return EXIT_SUCCESS;