forked from qemu/qemu
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
util: Add qemu_guest_getrandom and associated routines
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. Reviewed-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
- Loading branch information
Showing
3 changed files
with
162 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
* | ||
* 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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |