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 <[email protected]>
Signed-off-by: Fabiano Rosas <[email protected]>
---
 migration/vmstate.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/migration/vmstate.c b/migration/vmstate.c
index 1d028bfe009..08548ae7a1f 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -113,14 +113,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) {
         /*
@@ -132,7 +138,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,
@@ -364,7 +371,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;
@@ -682,9 +693,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.
@@ -692,6 +704,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;
-- 
2.53.0


Reply via email to