@samitouri / QOSamiQemu / commits / ac7fa2e9d4

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>

Peter Xu committed Jul 28, 2026 at 17:04 UTC ac7fa2e9d457ff9c777be32617f3c46548c4cadf
2 files changed +20 -2
migration/multifd-zlib.c
+10 -1
@@ -216,10 +216,19 @@ static int multifd_zlib_recv(MultiFDRecvParams *p, Error **errp)
216 return -1;
217 }
218
219 + if (in_size > z->zbuff_len) {
220 + error_setg(errp, "multifd %u: next_packet_size %"PRIu32
221 + " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len);
222 + return -1;
223 + }
224 +
225 multifd_recv_zero_page_process(p);
226
227 if (!p->normal_num) {
222 - assert(in_size == 0);
228 + if (in_size != 0) {
229 + error_setg(errp, "multifd %u: expected empty packet", p->id);
230 + return -1;
231 + }
232 return 0;
233 }
234
migration/multifd-zstd.c
+10 -1
@@ -210,10 +210,19 @@ static int multifd_zstd_recv(MultiFDRecvParams *p, Error **errp)
210 return -1;
211 }
212
213 + if (in_size > z->zbuff_len) {
214 + error_setg(errp, "multifd %u: next_packet_size %"PRIu32
215 + " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len);
216 + return -1;
217 + }
218 +
219 multifd_recv_zero_page_process(p);
220
221 if (!p->normal_num) {
216 - assert(in_size == 0);
222 + if (in_size != 0) {
223 + error_setg(errp, "multifd %u: expected empty packet", p->id);
224 + return -1;
225 + }
226 return 0;
227 }
228