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>
Fabiano Rosas committed
Aug 18, 2026 at 15:24 UTC
595273f1b71e7de40867223d1566be8355353921
1 file changed
+22
-6
migration/vmstate.c
+22
-6
@@ -126,14 +126,20 @@ static uint64_t vmstate_n_elems(void *opaque, const VMStateField *field)
126
return n_elems;
127
}
128
129
-static uint64_t vmstate_size(void *opaque, const VMStateField *field)
129
+static bool vmstate_size(void *opaque, const VMStateField *field,
130
+ uint64_t *sz, Error **errp)
131
{
132
uint64_t size;
133
134
+ *sz = 0;
135
+
136
if (field->flags & VMS_VBUFFER) {
137
size = vmstate_read_from_offset(&field->size_indirect, opaque);
135
- if (field->flags & VMS_MULTIPLY) {
136
- size *= field->size;
138
+ if ((field->flags & VMS_MULTIPLY) &&
139
+ umul64_overflow(size, field->size, &size)) {
140
+ error_setg(errp, "%s: VMState field '%s' multiply overflow",
141
+ __func__, field->name);
142
+ return false;
143
}
144
} else if (field->flags & VMS_ARRAY_OF_POINTER) {
145
/*
@@ -145,7 +151,8 @@ static uint64_t vmstate_size(void *opaque, const VMStateField *field)
151
size = field->size;
152
}
153
148
- return size;
154
+ *sz = size;
155
+ return true;
156
}
157
158
static bool vmstate_handle_alloc(void *ptr, const VMStateField *field,
@@ -377,7 +384,11 @@ bool vmstate_load_vmsd(QEMUFile *f, const VMStateDescription *vmsd,
384
void *first_elem = opaque + field->offset;
385
int i;
386
uint64_t n_elems = vmstate_n_elems(opaque, field);
380
- uint64_t size = vmstate_size(opaque, field);
387
+ uint64_t size;
388
+
389
+ if (!vmstate_size(opaque, field, &size, errp)) {
390
+ return false;
391
+ }
392
393
if (!vmstate_handle_alloc(first_elem, field, n_elems, size, errp)) {
394
return false;
@@ -695,9 +706,10 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
706
void *first_elem = opaque + field->offset;
707
int i;
708
uint64_t n_elems = vmstate_n_elems(opaque, field);
698
- uint64_t size = vmstate_size(opaque, field);
709
+ uint64_t size;
710
JSONWriter *vmdesc_loop = vmdesc;
711
bool is_prev_null = false;
712
+
713
/*
714
* When this is enabled, it means we will always push a ptr
715
* 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,
717
bool use_dynamic_array =
718
field->flags & VMS_ARRAY_OF_POINTER_AUTO_ALLOC;
719
720
+ if (!vmstate_size(opaque, field, &size, errp)) {
721
+ return false;
722
+ }
723
+
724
trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
725
if (field->flags & VMS_POINTER) {
726
first_elem = *(void **)first_elem;