Browse Source

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>
master
Richard Henderson 3 months ago
parent
commit
8d8404f156
3 changed files with 162 additions and 0 deletions
  1. 68
    0
      include/qemu/guest-random.h
  2. 1
    0
      util/Makefile.objs
  3. 93
    0
      util/guest-random.c

+ 68
- 0
include/qemu/guest-random.h View File

@@ -0,0 +1,68 @@
1
+/*
2
+ * QEMU guest-visible random functions
3
+ *
4
+ * Copyright 2019 Linaro, Ltd.
5
+ *
6
+ * This program is free software; you can redistribute it and/or modify it
7
+ * under the terms of the GNU General Public License as published by the Free
8
+ * Software Foundation; either version 2 of the License, or (at your option)
9
+ * any later version.
10
+ */
11
+
12
+#ifndef QEMU_GUEST_RANDOM_H
13
+#define QEMU_GUEST_RANDOM_H
14
+
15
+/**
16
+ * qemu_guest_random_seed_main(const char *optarg, Error **errp)
17
+ * @optarg: a non-NULL pointer to a C string
18
+ * @errp: an error indicator
19
+ *
20
+ * The @optarg value is that which accompanies the -seed argument.
21
+ * This forces qemu_guest_getrandom into deterministic mode.
22
+ *
23
+ * Returns 0 on success, < 0 on failure while setting *errp.
24
+ */
25
+int qemu_guest_random_seed_main(const char *optarg, Error **errp);
26
+
27
+/**
28
+ * qemu_guest_random_seed_thread_part1(void)
29
+ *
30
+ * If qemu_getrandom is in deterministic mode, returns an
31
+ * independent seed for the new thread.  Otherwise returns 0.
32
+ */
33
+uint64_t qemu_guest_random_seed_thread_part1(void);
34
+
35
+/**
36
+ * qemu_guest_random_seed_thread_part2(uint64_t seed)
37
+ * @seed: a value for the new thread.
38
+ *
39
+ * If qemu_guest_getrandom is in deterministic mode, this stores an
40
+ * independent seed for the new thread.  Otherwise a no-op.
41
+ */
42
+void qemu_guest_random_seed_thread_part2(uint64_t seed);
43
+
44
+/**
45
+ * qemu_guest_getrandom(void *buf, size_t len, Error **errp)
46
+ * @buf: a buffer of bytes to be written
47
+ * @len: the number of bytes in @buf
48
+ * @errp: an error indicator
49
+ *
50
+ * Fills len bytes in buf with random data.  This should only be used
51
+ * for data presented to the guest.  Host-side crypto services should
52
+ * use qcrypto_random_bytes.
53
+ *
54
+ * Returns 0 on success, < 0 on failure while setting *errp.
55
+ */
56
+int qemu_guest_getrandom(void *buf, size_t len, Error **errp);
57
+
58
+/**
59
+ * qemu_guest_getrandom_nofail(void *buf, size_t len)
60
+ * @buf: a buffer of bytes to be written
61
+ * @len: the number of bytes in @buf
62
+ *
63
+ * Like qemu_guest_getrandom, but will assert for failure.
64
+ * Use this when there is no reasonable recovery.
65
+ */
66
+void qemu_guest_getrandom_nofail(void *buf, size_t len);
67
+
68
+#endif /* QEMU_GUEST_RANDOM_H */

+ 1
- 0
util/Makefile.objs View File

@@ -54,5 +54,6 @@ util-obj-y += iova-tree.o
54 54
 util-obj-$(CONFIG_INOTIFY1) += filemonitor-inotify.o
55 55
 util-obj-$(CONFIG_LINUX) += vfio-helpers.o
56 56
 util-obj-$(CONFIG_OPENGL) += drm.o
57
+util-obj-y += guest-random.o
57 58
 
58 59
 stub-obj-y += filemonitor-stub.o

+ 93
- 0
util/guest-random.c View File

@@ -0,0 +1,93 @@
1
+/*
2
+ * QEMU guest-visible random functions
3
+ *
4
+ * Copyright 2019 Linaro, Ltd.
5
+ *
6
+ * This program is free software; you can redistribute it and/or modify it
7
+ * under the terms of the GNU General Public License as published by the Free
8
+ * Software Foundation; either version 2 of the License, or (at your option)
9
+ * any later version.
10
+ */
11
+
12
+#include "qemu/osdep.h"
13
+#include "qemu-common.h"
14
+#include "qemu/cutils.h"
15
+#include "qapi/error.h"
16
+#include "qemu/guest-random.h"
17
+#include "crypto/random.h"
18
+
19
+
20
+static __thread GRand *thread_rand;
21
+static bool deterministic;
22
+
23
+
24
+static int glib_random_bytes(void *buf, size_t len)
25
+{
26
+    GRand *rand = thread_rand;
27
+    size_t i;
28
+    uint32_t x;
29
+
30
+    if (unlikely(rand == NULL)) {
31
+        /* Thread not initialized for a cpu, or main w/o -seed.  */
32
+        thread_rand = rand = g_rand_new();
33
+    }
34
+
35
+    for (i = 0; i + 4 <= len; i += 4) {
36
+        x = g_rand_int(rand);
37
+        __builtin_memcpy(buf + i, &x, 4);
38
+    }
39
+    if (i < len) {
40
+        x = g_rand_int(rand);
41
+        __builtin_memcpy(buf + i, &x, i - len);
42
+    }
43
+    return 0;
44
+}
45
+
46
+int qemu_guest_getrandom(void *buf, size_t len, Error **errp)
47
+{
48
+    if (unlikely(deterministic)) {
49
+        /* Deterministic implementation using Glib's Mersenne Twister.  */
50
+        return glib_random_bytes(buf, len);
51
+    } else {
52
+        /* Non-deterministic implementation using crypto routines.  */
53
+        return qcrypto_random_bytes(buf, len, errp);
54
+    }
55
+}
56
+
57
+void qemu_guest_getrandom_nofail(void *buf, size_t len)
58
+{
59
+    qemu_guest_getrandom(buf, len, &error_fatal);
60
+}
61
+
62
+uint64_t qemu_guest_random_seed_thread_part1(void)
63
+{
64
+    if (deterministic) {
65
+        uint64_t ret;
66
+        glib_random_bytes(&ret, sizeof(ret));
67
+        return ret;
68
+    }
69
+    return 0;
70
+}
71
+
72
+void qemu_guest_random_seed_thread_part2(uint64_t seed)
73
+{
74
+    g_assert(thread_rand == NULL);
75
+    if (deterministic) {
76
+        thread_rand =
77
+            g_rand_new_with_seed_array((const guint32 *)&seed,
78
+                                       sizeof(seed) / sizeof(guint32));
79
+    }
80
+}
81
+
82
+int qemu_guest_random_seed_main(const char *optarg, Error **errp)
83
+{
84
+    unsigned long long seed;
85
+    if (parse_uint_full(optarg, &seed, 0)) {
86
+        error_setg(errp, "Invalid seed number: %s", optarg);
87
+        return -1;
88
+    } else {
89
+        deterministic = true;
90
+        qemu_guest_random_seed_thread_part2(seed);
91
+        return 0;
92
+    }
93
+}

Loading…
Cancel
Save