Commit 595273f1b7 for qemu.org
commit 595273f1b71e7de40867223d1566be8355353921
Author: Fabiano Rosas <farosas@suse.de>
Date: Tue Aug 18 15:24:41 2026 -0300
migration: Harden vmstate_size
Protect vmstate_size against overflow of the on-stack 64bit integer
during multiplication. The data at size_indirect.offset comes from the
migration stream.
Acked-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
diff --git a/migration/vmstate.c b/migration/vmstate.c
index cbcd86e5b6..24004565bf 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -126,14 +126,20 @@ static uint64_t vmstate_n_elems(void *opaque, const VMStateField *field)
return n_elems;
}
-static uint64_t vmstate_size(void *opaque, const VMStateField *field)
+static bool vmstate_size(void *opaque, const VMStateField *field,
+ uint64_t *sz, Error **errp)
{
uint64_t size;
+ *sz = 0;
+
if (field->flags & VMS_VBUFFER) {
size = vmstate_read_from_offset(&field->size_indirect, opaque);
- if (field->flags & VMS_MULTIPLY) {
- size *= field->size;
+ if ((field->flags & VMS_MULTIPLY) &&
+ umul64_overflow(size, field->size, &size)) {
+ error_setg(errp, "%s: VMState field '%s' multiply overflow",
+ __func__, field->name);
+ return false;
}
} else if (field->flags & VMS_ARRAY_OF_POINTER) {
/*
@@ -145,7 +151,8 @@ static uint64_t vmstate_size(void *opaque, const VMStateField *field)
size = field->size;
}
- return size;
+ *sz = size;
+ return true;
}
static bool vmstate_handle_alloc(void *ptr, const VMStateField *field,
@@ -377,7 +384,11 @@ bool vmstate_load_vmsd(QEMUFile *f, const VMStateDescription *vmsd,
void *first_elem = opaque + field->offset;
int i;
uint64_t n_elems = vmstate_n_elems(opaque, field);
- uint64_t size = vmstate_size(opaque, field);
+ uint64_t size;
+
+ if (!vmstate_size(opaque, field, &size, errp)) {
+ return false;
+ }
if (!vmstate_handle_alloc(first_elem, field, n_elems, size, errp)) {
return false;
@@ -695,9 +706,10 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
void *first_elem = opaque + field->offset;
int i;
uint64_t n_elems = vmstate_n_elems(opaque, field);
- uint64_t size = vmstate_size(opaque, field);
+ uint64_t size;
JSONWriter *vmdesc_loop = vmdesc;
bool is_prev_null = false;
+
/*
* When this is enabled, it means we will always push a ptr
* marker first for each element saying if it's populated.
@@ -705,6 +717,10 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
bool use_dynamic_array =
field->flags & VMS_ARRAY_OF_POINTER_AUTO_ALLOC;
+ if (!vmstate_size(opaque, field, &size, errp)) {
+ return false;
+ }
+
trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
if (field->flags & VMS_POINTER) {
first_elem = *(void **)first_elem;