On 3/15/19 4:26 AM, Richard Henderson wrote: > This routine is intended to produce high-quality random numbers to the > guest. Normally, such numbers are crypto quality from the host, but a > command-line option can force the use of a fully deterministic sequence > for use while debugging. > > Cc: Daniel P. Berrangé <berra...@redhat.com> > Signed-off-by: Richard Henderson <richard.hender...@linaro.org> > --- > include/qemu/guest-random.h | 68 +++++++++++++++++++++++++++ > util/guest-random.c | 93 +++++++++++++++++++++++++++++++++++++ > util/Makefile.objs | 1 + > 3 files changed, 162 insertions(+) > create mode 100644 include/qemu/guest-random.h > create mode 100644 util/guest-random.c > > diff --git a/include/qemu/guest-random.h b/include/qemu/guest-random.h > new file mode 100644 > index 0000000000..09ff9c2236 > --- /dev/null > +++ b/include/qemu/guest-random.h > @@ -0,0 +1,68 @@ > +/* > + * QEMU guest-visible random functions > + * > + * Copyright 2019 Linaro, Ltd. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the Free > + * Software Foundation; either version 2 of the License, or (at your option) > + * any later version. > + */ > + > +#ifndef QEMU_GUEST_RANDOM_H > +#define QEMU_GUEST_RANDOM_H > + > +/** > + * qemu_guest_random_seed_main(const char *optarg, Error **errp) > + * @optarg: a non-NULL pointer to a C string > + * @errp: an error indicator > + * > + * The @optarg value is that which accompanies the -seed argument. > + * This forces qemu_guest_getrandom into deterministic mode. > + * > + * Returns 0 on success, < 0 on failure while setting *errp. > + */ > +int qemu_guest_random_seed_main(const char *optarg, Error **errp); > + > +/** > + * qemu_guest_random_seed_thread_part1(void) > + * > + * If qemu_getrandom is in deterministic mode, returns an > + * independent seed for the new thread. Otherwise returns 0. > + */ > +uint64_t qemu_guest_random_seed_thread_part1(void); > + > +/** > + * qemu_guest_random_seed_thread_part2(uint64_t seed) > + * @seed: a value for the new thread. > + * > + * If qemu_guest_getrandom is in deterministic mode, this stores an > + * independent seed for the new thread. Otherwise a no-op. > + */ > +void qemu_guest_random_seed_thread_part2(uint64_t seed); > + > +/** > + * qemu_guest_getrandom(void *buf, size_t len, Error **errp) > + * @buf: a buffer of bytes to be written > + * @len: the number of bytes in @buf > + * @errp: an error indicator > + * > + * Fills len bytes in buf with random data. This should only be used > + * for data presented to the guest. Host-side crypto services should > + * use qcrypto_random_bytes. > + * > + * Returns 0 on success, < 0 on failure while setting *errp. > + */ > +int qemu_guest_getrandom(void *buf, size_t len, Error **errp); > + > +/** > + * qemu_guest_getrandom_nofail(void *buf, size_t len) > + * @buf: a buffer of bytes to be written > + * @len: the number of bytes in @buf
API documentation appreciated, thanks! Reviewed-by: Philippe Mathieu-Daudé <phi...@redhat.com> > + * > + * Like qemu_guest_getrandom, but will assert for failure. > + * Use this when there is no reasonable recovery. > + */ > +void qemu_guest_getrandom_nofail(void *buf, size_t len); > + > +#endif /* QEMU_GUEST_RANDOM_H */ > diff --git a/util/guest-random.c b/util/guest-random.c > new file mode 100644 > index 0000000000..e8124a3cad > --- /dev/null > +++ b/util/guest-random.c > @@ -0,0 +1,93 @@ > +/* > + * QEMU guest-visible random functions > + * > + * Copyright 2019 Linaro, Ltd. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the Free > + * Software Foundation; either version 2 of the License, or (at your option) > + * any later version. > + */ > + > +#include "qemu/osdep.h" > +#include "qemu-common.h" > +#include "qemu/cutils.h" > +#include "qapi/error.h" > +#include "qemu/guest-random.h" > +#include "crypto/random.h" > + > + > +static __thread GRand *thread_rand; > +static bool deterministic; > + > + > +static int glib_random_bytes(void *buf, size_t len) > +{ > + GRand *rand = thread_rand; > + size_t i; > + uint32_t x; > + > + if (unlikely(rand == NULL)) { > + /* Thread not initialized for a cpu, or main w/o -seed. */ > + thread_rand = rand = g_rand_new(); > + } > + > + for (i = 0; i + 4 <= len; i += 4) { > + x = g_rand_int(rand); > + __builtin_memcpy(buf + i, &x, 4); > + } > + if (i < len) { > + x = g_rand_int(rand); > + __builtin_memcpy(buf + i, &x, i - len); > + } > + return 0; > +} > + > +int qemu_guest_getrandom(void *buf, size_t len, Error **errp) > +{ > + if (unlikely(deterministic)) { > + /* Deterministic implementation using Glib's Mersenne Twister. */ > + return glib_random_bytes(buf, len); > + } else { > + /* Non-deterministic implementation using crypto routines. */ > + return qcrypto_random_bytes(buf, len, errp); > + } > +} > + > +void qemu_guest_getrandom_nofail(void *buf, size_t len) > +{ > + qemu_guest_getrandom(buf, len, &error_fatal); > +} > + > +uint64_t qemu_guest_random_seed_thread_part1(void) > +{ > + if (deterministic) { > + uint64_t ret; > + glib_random_bytes(&ret, sizeof(ret)); > + return ret; > + } > + return 0; > +} > + > +void qemu_guest_random_seed_thread_part2(uint64_t seed) > +{ > + g_assert(thread_rand == NULL); > + if (deterministic) { > + thread_rand = > + g_rand_new_with_seed_array((const guint32 *)&seed, > + sizeof(seed) / sizeof(guint32)); > + } > +} > + > +int qemu_guest_random_seed_main(const char *optarg, Error **errp) > +{ > + unsigned long long seed; > + if (parse_uint_full(optarg, &seed, 0)) { > + error_setg(errp, "Invalid seed number: %s", optarg); > + return -1; > + } else { > + deterministic = true; > + qemu_guest_random_seed_thread_part2(seed); > + return 0; > + } > +} > diff --git a/util/Makefile.objs b/util/Makefile.objs > index 835fcd69e2..4d4db653cc 100644 > --- a/util/Makefile.objs > +++ b/util/Makefile.objs > @@ -53,5 +53,6 @@ util-obj-y += iova-tree.o > util-obj-$(CONFIG_INOTIFY1) += filemonitor-inotify.o > util-obj-$(CONFIG_LINUX) += vfio-helpers.o > util-obj-$(CONFIG_OPENGL) += drm.o > +util-obj-y += guest-random.o > > stub-obj-y += filemonitor-stub.o >