We only allow access to the "urandom" side of the interface, and using -seed forces the use of the deterministic algorithm.
Signed-off-by: Richard Henderson <richard.hender...@linaro.org> --- util/random.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++----- configure | 18 +++++++++++- 2 files changed, 88 insertions(+), 8 deletions(-) diff --git a/util/random.c b/util/random.c index ded8725a3b..833169fad5 100644 --- a/util/random.c +++ b/util/random.c @@ -15,6 +15,13 @@ #include "qapi/error.h" #include "qemu/random.h" +#ifdef CONFIG_GETRANDOM +# include <sys/random.h> +static bool deterministic; +#else +#define deterministic true +#endif + /* * While jrand48 is not technically thread safe, jrand48_r is glibc specific. @@ -25,13 +32,11 @@ static __thread uint16_t xsubi[3]; /* Deterministic implementation using libc functions. */ -bool qemu_getrandom(void *buf, size_t len, bool nonblock) +static bool do_jrand48(void *buf, size_t len, bool nonblock) { size_t i; uint32_t val; - g_assert_cmpuint(len, <=, 256); - for (i = 0; i + 4 <= len; i += 4) { val = jrand48(xsubi); __builtin_memcpy(buf + i, &val, 4); @@ -44,18 +49,63 @@ bool qemu_getrandom(void *buf, size_t len, bool nonblock) return true; } +#ifdef CONFIG_GETRANDOM +static bool do_getrandom(void *buf, size_t len, bool nonblock) +{ + while (len != 0) { + ssize_t ret = getrandom(buf, len, nonblock ? GRND_NONBLOCK : 0); + if (unlikely(ret < 0)) { + switch (errno) { + case EAGAIN: + /* Only returned for GRND_NONBLOCK. */ + return false; + case EINTR: + /* Signal. Just try again. */ + break; + default: + /* EFAULT or EINVAL; either a bug in the user or here. */ + g_assert_not_reached(); + } + } else { + len -= ret; + buf += ret; + } + } + return true; +} +#endif + +bool qemu_getrandom(void *buf, size_t len, bool nonblock) +{ + /* Assert the interface contract is honored. */ + g_assert_cmpuint(len, <=, 256); + + if (!deterministic) { +#ifdef CONFIG_GETRANDOM + return do_getrandom(buf, len, nonblock); +#endif + } + return do_jrand48(buf, len, nonblock); +} + uint64_t qemu_seedrandom_thread_part1(void) { uint64_t ret; - qemu_getrandom(&ret, sizeof(ret), false); + if (deterministic) { + qemu_getrandom(&ret, sizeof(ret), false); + } else { + ret = 0; + } return ret; } void qemu_seedrandom_thread_part2(uint64_t seed) { - xsubi[0] = seed; - xsubi[1] = seed >> 16; - xsubi[2] = seed >> 32; + if (deterministic) { + xsubi[0] = seed; + xsubi[1] = seed >> 16; + xsubi[2] = seed >> 32; + } } void qemu_seedrandom_main(const char *optarg, Error **errp) @@ -64,6 +114,9 @@ void qemu_seedrandom_main(const char *optarg, Error **errp) if (parse_uint_full(optarg, &seed, 0)) { error_setg(errp, "Invalid seed number: %s", optarg); } else { +#ifndef deterministic + deterministic = true; +#endif qemu_seedrandom_thread_part2(seed); } } @@ -72,5 +125,16 @@ static void __attribute__((constructor)) initialize(void) { /* Make sure A and C parameters are initialized. */ srand48(0); + +#ifdef CONFIG_GETRANDOM + /* Make sure support exists within the running kernel. */ + errno = 0; + if (getrandom(NULL, 0, 0) == 0) { + return; + } + g_assert_cmpint(errno, ==, ENOSYS); + deterministic = true; +#endif + qemu_seedrandom_thread_part2(time(NULL) + getpid() * 1500450271ull); } diff --git a/configure b/configure index cab830a4c9..22c7944e38 100755 --- a/configure +++ b/configure @@ -5700,6 +5700,20 @@ if compile_prog "" "" ; then have_utmpx=yes fi +########################################## +# check for getrandom() + +have_getrandom=no +cat > $TMPC << EOF +#include <sys/random.h> +int main(void) { + return getrandom(0, 0, GRND_NONBLOCK); +} +EOF +if compile_prog "" "" ; then + have_getrandom=yes +fi + ########################################## # checks for sanitizers @@ -7073,7 +7087,9 @@ fi if test "$have_utmpx" = "yes" ; then echo "HAVE_UTMPX=y" >> $config_host_mak fi - +if test "$have_getrandom" = "yes" ; then + echo "CONFIG_GETRANDOM=y" >> $config_host_mak +fi if test "$ivshmem" = "yes" ; then echo "CONFIG_IVSHMEM=y" >> $config_host_mak fi -- 2.17.1