Commit ac7fa2e9d4 for qemu.org

commit ac7fa2e9d457ff9c777be32617f3c46548c4cadf
Author: Peter Xu <peterx@redhat.com>
Date:   Tue Jul 28 17:04:14 2026 -0400

    migration/multifd: Validate next_packet_size in zlib/zstd recv

    The zlib and zstd multifd compression backends read next_packet_size from
    the incoming migration stream and use it directly as the read length into a
    fixed-size buffer (MULTIFD_PACKET_SIZE * 2 = 1MB).  A malicious migration
    source can set next_packet_size bigger than allocated, causing a heap
    buffer overflow write on the destination.

    Add a check against zbuff_len before reading, matching what the qatzip
    backend already does.  Also replace the assert(in_size == 0) for empty
    packets with proper error reporting, since the value is wire-controlled,
    meanwhile assert() stops working with -DNDEBUG builds.

    Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3737
    Reported-by: xlabai <xlabai@tencent.com>
    Reported-by: Jules Denardou <jules.denardou@datadoghq.com>
    Reported-by: Tristan Madani <tristan@talencesecurity.com>
    Reported-by: david korczynski (@david1766)
    Reported-by: huntr bubble (@bubblehuntr)
    Cc: qemu-stable <qemu-stable@nongnu.org>
    Reviewed-by: Fabiano Rosas <farosas@suse.de>
    Link: https://lore.kernel.org/r/20260728210417.1925078-3-peterx@redhat.com
    Signed-off-by: Peter Xu <peterx@redhat.com>

diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c
index 8820b2a787..400146566e 100644
--- a/migration/multifd-zlib.c
+++ b/migration/multifd-zlib.c
@@ -216,10 +216,19 @@ static int multifd_zlib_recv(MultiFDRecvParams *p, Error **errp)
         return -1;
     }

+    if (in_size > z->zbuff_len) {
+        error_setg(errp, "multifd %u: next_packet_size %"PRIu32
+                   " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len);
+        return -1;
+    }
+
     multifd_recv_zero_page_process(p);

     if (!p->normal_num) {
-        assert(in_size == 0);
+        if (in_size != 0) {
+            error_setg(errp, "multifd %u: expected empty packet", p->id);
+            return -1;
+        }
         return 0;
     }

diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c
index 3c2dcf76b0..69ef1a5f38 100644
--- a/migration/multifd-zstd.c
+++ b/migration/multifd-zstd.c
@@ -210,10 +210,19 @@ static int multifd_zstd_recv(MultiFDRecvParams *p, Error **errp)
         return -1;
     }

+    if (in_size > z->zbuff_len) {
+        error_setg(errp, "multifd %u: next_packet_size %"PRIu32
+                   " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len);
+        return -1;
+    }
+
     multifd_recv_zero_page_process(p);

     if (!p->normal_num) {
-        assert(in_size == 0);
+        if (in_size != 0) {
+            error_setg(errp, "multifd %u: expected empty packet", p->id);
+            return -1;
+        }
         return 0;
     }