@samitouri / QOSamiQemu / commits / 595273f1b7

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;