@@ -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);
}
@@ -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
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.henderson@linaro.org> --- util/random.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++----- configure | 18 +++++++++++- 2 files changed, 88 insertions(+), 8 deletions(-) -- 2.17.1