@samitouri / QOSamiQemu / commits / acba2d7817

hw/uefi: add post_load checks

Add sanity checks to uefi-vars state loaded from live migration data stream. Fail migration if invalid data or inconsistencies are found. Fixes: CVE-2026-61404 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3837 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3838 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3839 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3885 Acked-by: Luigi Leonardi <leonardi@redhat.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Message-ID: <20260720143244.821889-6-kraxel@redhat.com>

Gerd Hoffmann committed Jul 20, 2026 at 16:32 UTC acba2d78176d8235b81fd886b37642ae6c464982
4 files changed +56 -6
hw/uefi/var-service-core.c
+13 -1
@@ -5,6 +5,7 @@
5 */
6 #include "qemu/osdep.h"
7 #include "qemu/crc32c.h"
8 +#include "qemu/error-report.h"
9 #include "system/dma.h"
10 #include "migration/vmstate.h"
11
@@ -28,9 +29,20 @@ static int uefi_vars_post_load(void *opaque, int version_id)
29 {
30 uefi_vars_state *uv = opaque;
31
32 + if (uv->buf_size > MAX_BUFFER_SIZE) {
33 + error_report("invalid buffer size");
34 + return -1;
35 + }
36 + uv->buffer = g_malloc(uv->buf_size);
37 +
38 uefi_vars_update_storage(uv);
39 + if (uv->used_storage > uv->max_storage) {
40 + error_report("out of variable memory (%" PRId64 " > %" PRId64 ")",
41 + uv->used_storage, uv->max_storage);
42 + return -1;
43 + }
44 +
45 uefi_vars_json_save(uv);
33 - uv->buffer = g_malloc(uv->buf_size);
46 return 0;
47 }
48
hw/uefi/var-service-json.c
+5
@@ -18,6 +18,7 @@
18 #include "qobject/qobject.h"
19 #include "qobject/qjson.h"
20
21 +#include "qapi/error.h"
22 #include "qapi/dealloc-visitor.h"
23 #include "qapi/qobject-input-visitor.h"
24 #include "qapi/qobject-output-visitor.h"
@@ -249,6 +250,10 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp)
250 if (!(*errp)) {
251 uefi_vars_from_qapi(uv, vs);
252 uefi_vars_update_storage(uv);
253 + if (uv->used_storage > uv->max_storage) {
254 + error_setg(errp, "out of variable memory (%" PRId64 " > %" PRId64 ")",
255 + uv->used_storage, uv->max_storage);
256 + }
257 }
258
259 qapi_free_UefiVarStore(vs);
hw/uefi/var-service-policy.c
+23 -5
@@ -7,6 +7,7 @@
7 * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
8 */
9 #include "qemu/osdep.h"
10 +#include "qemu/error-report.h"
11 #include "system/dma.h"
12 #include "migration/vmstate.h"
13
@@ -16,14 +17,18 @@
17
18 #include "trace.h"
19
19 -static void calc_policy(uefi_var_policy *pol);
20 +static int check_calc_policy(uefi_var_policy *pol);
21
22 static int uefi_var_policy_post_load(void *opaque, int version_id)
23 {
24 uefi_var_policy *pol = opaque;
25 + int rc;
26
25 - calc_policy(pol);
26 - return 0;
27 + rc = check_calc_policy(pol);
28 + if (rc != 0) {
29 + error_report("invalid uefi variable policy");
30 + }
31 + return rc;
32 }
33
34 const VMStateDescription vmstate_uefi_var_policy = {
@@ -80,32 +85,45 @@ static uefi_var_policy *wildcard_find_policy(uefi_vars_state *uv,
85 return NULL;
86 }
87
83 -static void calc_policy(uefi_var_policy *pol)
88 +static int check_calc_policy(uefi_var_policy *pol)
89 {
90 variable_policy_entry *pe = pol->entry;
91 unsigned int i;
92
93 + if (pol->entry_size != pe->size ||
94 + pe->offset_to_name >= pe->size) {
95 + return -1;
96 + }
97 +
98 pol->name = (void *)pol->entry + pe->offset_to_name;
99 pol->name_size = pe->size - pe->offset_to_name;
100
101 + if (!uefi_str_is_valid(pol->name, pol->name_size, false)) {
102 + return -1;
103 + }
104 +
105 for (i = 0; i < pol->name_size / 2; i++) {
106 if (pol->name[i] == '#') {
107 pol->hashmarks++;
108 }
109 }
110 +
111 + return 0;
112 }
113
114 uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv,
115 variable_policy_entry *pe)
116 {
117 uefi_var_policy *pol, *p;
118 + int rc;
119
120 pol = g_new0(uefi_var_policy, 1);
121 pol->entry = g_malloc(pe->size);
122 memcpy(pol->entry, pe, pe->size);
123 pol->entry_size = pe->size;
124
108 - calc_policy(pol);
125 + rc = check_calc_policy(pol);
126 + g_assert(rc == 0);
127
128 /* keep list sorted by priority, add to tail of priority group */
129 QTAILQ_FOREACH(p, &uv->var_policies, next) {
hw/uefi/var-service-vars.c
+15
@@ -47,6 +47,20 @@ static int uefi_vars_pre_load(void *opaque)
47 return 0;
48 }
49
50 +static int uefi_vars_post_load(void *opaque, int version_id)
51 +{
52 + uefi_variable *var = opaque;
53 +
54 + if (!uefi_str_is_valid(var->name, var->name_size, true) ||
55 + var->attributes & ~EFI_VARIABLE_ATTRIBUTE_SUPPORTED ||
56 + (var->digest_size != 0 &&
57 + var->digest_size != 32 /* AUTHVAR_DIGEST_SIZE */)) {
58 + error_report("invalid uefi variable");
59 + return -1;
60 + }
61 + return 0;
62 +}
63 +
64 static bool uefi_vars_digest_is_needed(void *opaque)
65 {
66 uefi_variable *var = opaque;
@@ -72,6 +86,7 @@ const VMStateDescription vmstate_uefi_variable_digest = {
86 const VMStateDescription vmstate_uefi_variable = {
87 .name = "uefi-variable",
88 .pre_load = uefi_vars_pre_load,
89 + .post_load = uefi_vars_post_load,
90 .fields = (VMStateField[]) {
91 VMSTATE_UINT8_ARRAY_V(guid.data, uefi_variable, sizeof(QemuUUID), 0),
92 VMSTATE_UINT32(name_size, uefi_variable),